#include "mainctrl.h"
#include "cmd/cmdupdatedata.h"
#include "cmd/cmdselectdatafromdbsvr.h"
#include "cmdfront/cmduserinfo.h"
#include "cmdinner/CCmdMainctrlInitData.h"
#include "MsgDefend.pb.h"
#include "cmdinner/CCmdSendTips.h"
#include "cmdinner/CCmdSendMallGoods.h"
#include "cmdinner/CCmdInnerMarket.h"
#include "cmdinner/CCmdInnerCheckName.h"
#include "cmdinner/CCmdInnerCreateUser.h"

#include "cmd/cmdapi.h"
#include "MsgBattle.pb.h"
#include "net/CTMD5.h"

bool CMainCtrl::s_bReloadConfig = false;
bool CMainCtrl::s_bExit = false;
CMainCtrl*  CMainCtrl::m_pstMain = NULL;

void CMainCtrl::sigusr1_handle(int iSigVal)
{
    s_bReloadConfig = true;
    signal(SIGUSR1, CMainCtrl::sigusr1_handle);
}

void CMainCtrl::sigusr2_handle(int iSigVal)
{
    s_bExit = true;
    signal(SIGUSR2, CMainCtrl::sigusr2_handle);
}

CMainCtrl::CMainCtrl()
    : m_stTaskMgr(m_stTaskUserDataInf)
{
    m_pstMain = this;
    m_bDataIsInited = false;
    m_bResetShm = true;
    m_bIsSysBusy = false;
    m_szSendCache = NULL;

    m_stTaskUserDataInf.pstMainCtrl = this;

    // 统计项描述
    const char g_aszEnpStatDesc[][CTStatItem::CTLIB_DESC_MAX] =
    {
        "RecvMessage",
        "SendMessage",
        "Accept",
        "IdleClose",
        "MainLoop",
        "WaitTimeout",
        "Check",
        "AsyncCmds",
        "TimeoutCmds",
    };

    m_stStat.Init(CT_COUNTOF(g_aszEnpStatDesc), g_aszEnpStatDesc);
    m_stCmdStat.Init(10000);
}

CMainCtrl::~CMainCtrl()
{
}

int CMainCtrl::Init()
{
    int iRet = 0;
    m_bBeginRun = false;

    gettimeofday(&m_stNowTime, NULL);
    m_iNow = m_stNowTime.tv_sec;

    //安装信号处理函数
    signal(SIGUSR1, CMainCtrl::sigusr1_handle); // kill -USR1 pid 会让程序重读配置
    signal(SIGUSR2, CMainCtrl::sigusr2_handle); // kill -USR2 pid 会让程序安全的退出

    // 读取配置
    iRet = CONF->LoadConfig();
    if (iRet)
    {
        CT_ERROR(("load config error"));
        CHECK_THROW(iRet);
    }
    CT_INFO(("START......load config OK!"));

    // 创建Epoll
    iRet = m_stEpoll.Init(MAIN_EVENT_SIZE, MAIN_WAIT_SIZE);
    if (iRet)
    {
        CT_ERROR(("init epoll error"));
        CHECK_THROW(iRet);
    }
    CT_INFO(("START......create epoll OK!"));

    // 创建共享内存队列
    iRet = m_stShmQueue.Init(true,
        CONF->FrontEndShmKey(), CONF->FrontEndShmSize,
        CONF->FrontEndSocket(), CONF->BackEndSocket(),
        m_stEpoll,
        *this,
        &CMainCtrl::OnShmQueueEvent);
    if (iRet)
    {
        CT_ERROR(("init shm queue error"));
        CHECK_THROW(iRet);
    }
    CT_INFO(("START......create share memery queue OK!"));

    iRet = m_stDB.Init(*this, m_stEpoll);
    if (iRet)
    {
        CT_ERROR(("init connect to db error"));
        CHECK_THROW(iRet);
    }
    CT_INFO(("START......init db connect OK!"));

    iRet = m_stApi.Init(*this, m_stEpoll);
    if (iRet)
    {
        CT_ERROR(("init connect to api error"));
        CHECK_THROW(iRet);
    }
    CT_INFO(("START......init api connect OK!"));

    iRet = m_stMemoryDB.Init(*this, m_stEpoll);
    if (iRet)
    {
        CT_ERROR(("init connect to memory db error"));
        CHECK_THROW(iRet);
    }
    CT_INFO(("START......init memory db connect OK!"));

    iRet = m_stSenWordMgr.init( CONF->SenWordCfgFilePath() );
    if( iRet<0 )
    {
        CT_ERROR( ("init sensitive word config file error") );
        CHECK_THROW(1);
    }
    CT_INFO(("START......init sen word mgr OK!"));

    //初始化编解码buff
    m_stDecodeBuf.Attach(NULL, CSMsgDef::EMT_MAX_MSGLEN + sizeof(CEnpNetHead), 0);

    m_stEncodeBuf.Attach(NULL, CSMsgDef::EMT_MAX_MSGLEN + sizeof(CEnpNetHead), 0);

#ifdef _DEBUG
    const char* pszConfig = "(512,2000)(1024,1000)(4096,500)(32768,500)(65536,100)(200000,200)";
#elif defined _REG10000
    const char* pszConfig = "(512,4000)(1024,4000)(4096,4000)(32768,4000)(65536,400)(200000,200)";
#else
    //const char* pszConfig = "(512,20000)(1024,5000)(4096,4000)(32768,4000)(65536,3500)(200000,200)";
    const char* pszConfig = "(512,4000)(1024,4000)(4096,4000)(32768,4000)(65536,400)(200000,200)";
#endif

    //////////////////////////////////////////////////////////////////////////update mgr 更新开始//////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
    const char* pszUpdateConfig = "(512,1200)(1024,1500)(4096,150)(16384,20)(65536,20)(131072,10)";
#elif defined _REG10000
    const char* pszUpdateConfig = "(512,4000)(1024,2000)(2048,400)(4096,200)(16384,100)(32768,2)(131072,1)";
#else
    //const char* pszUpdateConfig = "(512,25000)(1024,2000)(2048,3000)(4096,8000)(16384,2000)(32768,1500)(131072,100)";
    const char* pszUpdateConfig = "(512,4000)(1024,2000)(2048,400)(4096,200)(16384,100)(32768,2)(131072,1)";
#endif

    //#ifdef _DEBUG
    ZeroPkgKey();
    //#endif

    if (!m_bResetShm)
    {
        OBJ_STAT->SetNotReset();
    }

    const size_t iUpdateMgrSize  = m_stUpdateMgr.CountSize(pszUpdateConfig);
    if (iUpdateMgrSize <= 0)
    {
        CT_ERROR(("invalid m_stUpdateMgr size %d", iUpdateMgrSize));
        return RET_SYS_ERROR;
    }

    const int iUptTotalSize = sizeof(int) + iUpdateMgrSize;

    iRet = m_stUpdateMgrShm.Create(CONF->UptShmKey(), iUptTotalSize);
    CHECK_RETURN(iRet);

    bool bResetUptShm = OBJ_STAT->IsReset();
    int* piOrgUptShmSize = (int*)m_stUpdateMgrShm.GetBuffer();
    if (!bResetUptShm && *piOrgUptShmSize != iUptTotalSize) //表示-u重启，需要恢复共享内存中的数据，但是共享内存中的长度和之前的不同
    {
        CT_WARNING(("upt shm size changed, -u param is invalid"));
        CHECK_THROW(RET_SYS_ERROR);
    }
    *piOrgUptShmSize = iUptTotalSize;

    iRet = m_stUpdateMgr.Init(CMainCtrl::UpdateData, pszUpdateConfig, m_stUpdateMgrShm.GetBuffer() + sizeof(int),
        CONF->InitDataFlushNum, CONF->MaxDataFlushNum, bResetUptShm);
    CHECK_THROW(iRet);

    CT_INFO(("START......init update mgr OK!"));

    //////////////////////////////////////////////////////////////////////////update mgr 更新结束//////////////////////////////////////////////////////////////////////////

    const size_t iFactorySize = CT_CMDFACTORY->CountSize(pszConfig);
    CT_INFO(("CT_CMDFACTORY shm size %ld", iFactorySize));

    const size_t iPlayerSnsCommInfoSize = m_stPlayerSnsCommInfoHash.CountSize(MAX_REGISTER_PLAYER);
    CT_INFO(("m_stPlayerSnsCommInfoHash shm size %ld", iPlayerSnsCommInfoSize));

    CZoneData::TraceSize();

    const size_t iPlayerBaseInfoSize = m_stPlayerBaseInfoHash.CountSize(USER_BASE_INFO_COUNT);
    CPlayerBaseInfo::TraceSize();
    CT_INFO(("m_stPlayerBaseInfoHash shm size %ld", iPlayerBaseInfoSize));

    const size_t iPlayerDetailInfoSize = m_stPlayerDetailInfoHash.CountSize(USER_DETAIL_INFO_COUNT);
    CPlayerDetailInfo::TraceSize();
    CT_INFO(("m_stPlayerDetailInfoHash shm size %ld", iPlayerDetailInfoSize));

    const size_t iPlayerDetailEx1InfoSize = m_stPlayerDetailEx1InfoHash.CountSize(USER_DETAIL_INFO_COUNT);
    CPlayerDetailInfoEx1::TraceSize();
    CT_INFO(("m_stPlayerDetailEx1InfoHash shm size %ld", iPlayerDetailEx1InfoSize));

    const size_t iUnactPartnerInfoSize = m_stUnactPartnerInfoHash.CountSize(UNACT_PARTNER_INFO_COUNT);
    CT_INFO(("m_stUnactPartnerInfoHash shm size %ld", iUnactPartnerInfoSize));

    const size_t iPlayerSnsDetailInfoSize = m_stPlayerSnsDetailInfoHash.CountSize(USER_SNS_DETAIL_INFO_COUNT);
    CT_INFO(("m_stPlayerSnsDetailInfoHash shm size %ld", iPlayerSnsDetailInfoSize));

    const size_t iInstanceZoneDetailInfoSize = m_stInstanceZoneDetailInfoHash.CountSize(MAX_INSTANCE_ZONE_DETAIL_INFO_COUNT);
    CT_INFO(("m_stInstanceZoneDetailInfoHash shm size %ld", iInstanceZoneDetailInfoSize));

    const size_t iRolenameHashSize = m_stRolenameHash.CountSize(MAX_REGISTER_PLAYER);
    CT_INFO(("m_stRolenameHash shm size %ld", iRolenameHashSize));

    const size_t iUserTokenToGID = m_stUserTokenToGID.CountSize(USER_TOKEN_TO_GID_COUNT);
    CT_INFO(("m_stUserTokenToGID shm size %ld", iUserTokenToGID));

    const size_t iPlayerOnlineSize = m_stPlayerOnline.CountSize(MAX_PLAYER_ONLINE);
    CT_INFO(("m_stPlayerOnline shm size %ld", iPlayerOnlineSize));

    const size_t iMapViewSize = m_stMapViews.CountSize();
    CT_INFO(("m_stMapViews shm size %ld", iMapViewSize));

    const size_t iEquipSize = m_stEquipMgr.CountSize();
    CT_INFO(("m_stEquipMgr shm size %ld", iEquipSize));

    const size_t iPlayerOnlineDataSize = m_stPlayerOnlineDataHash.CountSize(MAX_PLAYER_ONLINE_DATA_COUNT)
        + CPlayerOnlineData::CountSize() * MAX_PLAYER_ONLINE_DATA_COUNT;
    CT_INFO(("m_stPlayerOnlineDataHash shm size %ld", iPlayerOnlineDataSize));

    const size_t iPlayerCacheDataSize = m_stPlayerCacheDataHash.CountSize(MAX_REGISTER_PLAYER)
        + CPlayerCacheData::CountSize() * MAX_REGISTER_PLAYER;
    CT_INFO(("m_stPlayerCacheDataHash shm size %ld", iPlayerCacheDataSize));

    const size_t iItemMgrSize = m_stItemMgr.CountSize();
    CT_INFO(("m_stItemMgr shm size %ld", iItemMgrSize));

    const size_t iSkillMgrSize = m_stSkillMgr.CountSize();
    CT_INFO(("m_stSkillMgr shm size %ld", iSkillMgrSize));

    const size_t iMonsterConfigMgrSize = m_stMonsterConfigMgr.CountSize();
    CT_INFO(("m_stMonsterConfigMgr shm size %ld", iMonsterConfigMgrSize));

    const size_t iInstanceMgrSize = m_stInstanceMgr.CountSize();
    CT_INFO(("m_stInstanceMgr shm size %ld", iInstanceMgrSize));

    const size_t iDrugMgrSize = m_stDrugMgr.CountSize();
    CT_INFO(("m_stDrugMgr shm size %ld", iDrugMgrSize));

    const size_t iArenaMgrSize = m_stArenaMgr.CountSize();
    CT_INFO(("m_stArenaMgr shm size %ld", iArenaMgrSize));

    const size_t iClanMgrSize = m_stClanMgr.CountSize();
    CT_INFO(("m_stClanMgr shm size %ld", iClanMgrSize));

    const size_t iStatusMgrSize = m_stStatusMgr.CountSize();
    CT_INFO(("m_stStatusMgr shm size %ld", iStatusMgrSize));

    const size_t iActMgrSize = m_stActMgr.CountSize();
    CT_INFO(("m_stActMgr shm size %ld", iActMgrSize));

    const size_t iSceneMgrSize = m_stSceneMgr.CountSize();
    CT_INFO(("m_stSceneMgr shm size %ld", iSceneMgrSize));

    const size_t iLessonMgrSize = m_stLessonMgr.CountSize();
    CT_INFO(("m_stLessonMgr shm size %ld", iLessonMgrSize));

    const size_t iClanBattleMgrSize = m_stClanBattleMgr.CountSize();
    CT_INFO(("m_stClanBattleMgr shm size %ld", iClanBattleMgrSize));

    const size_t iAuctionMgrSize = m_stAuctionMgr.CountSize();
    CT_INFO(("m_stAuctionMgr shm size %ld", iAuctionMgrSize));

    const size_t iMallMgrSize = m_stQQMallMgr.CountSize();
    CT_INFO(("m_stQQMallMgr shm size %ld", iMallMgrSize));

    const size_t iCommConfigLoaderSize = m_stCommConfigLoader.CountSize();
    CT_INFO(("m_stCommConfigLoader shm size %ld", iMallMgrSize));

    const size_t iAcaCupMgrSize = m_stAcaCupMgr.CountSize();
    CT_INFO(("m_stAcaCupMgr shm size %ld", iAcaCupMgrSize));

    const size_t iChatMgrSize = m_stChatMgr.CountSize();
    CT_INFO(("m_stChatMgr shm size %ld", iChatMgrSize));

    //计算共享内存大小
    const size_t iCountSize = sizeof(CShmCtrlInfo)
                            + iFactorySize
                            + iPlayerSnsCommInfoSize
                            + iPlayerBaseInfoSize
                            + iPlayerDetailInfoSize
                            + iPlayerDetailEx1InfoSize
                            + iUnactPartnerInfoSize
                            + iPlayerSnsDetailInfoSize
                            + iInstanceZoneDetailInfoSize
                            + iRolenameHashSize
                            + iUserTokenToGID
                            + iPlayerOnlineSize
                            + iMapViewSize
                            + iEquipSize
                            + iPlayerOnlineDataSize
                            + iPlayerCacheDataSize
                            + iItemMgrSize
                            + iMonsterConfigMgrSize
                            + iInstanceMgrSize
                            + iSkillMgrSize
                            + iDrugMgrSize
                            + iArenaMgrSize
                            + iClanMgrSize
                            + iStatusMgrSize
                            + iActMgrSize
                            + iSceneMgrSize
                            + iLessonMgrSize
                            + iClanBattleMgrSize
                            + iAuctionMgrSize
                            + iMallMgrSize
                            + iCommConfigLoaderSize
                            + iAcaCupMgrSize
                            + iChatMgrSize;

    //创建共享内存
    iRet = m_stMgrShm.Create(CONF->MgrShmKey(), iCountSize);
    CHECK_THROW(iRet);

    CT_INFO(("START......create share memery (size %d) OK!", iCountSize));

    //初始化
    size_t BufOffset = 0;

    //reload检查
    CShmCtrlInfo * pstShmCtrl = (CShmCtrlInfo*)m_stMgrShm.GetBuffer();
    if (!OBJ_STAT->IsReset() && pstShmCtrl->CheckNeedReset(iCountSize)) //表示-u重启，需要恢复共享内存中的数据，但是共享内存中的长度和之前的不同
    {
        CT_WARNING(("shm size changed, -u param is invalid"));
        OBJ_STAT->SetReset();
    }
    BufOffset += sizeof(CShmCtrlInfo);

    //开始初始化
    if (OBJ_STAT->IsReset())
    {
        CT_TRACE(("begin full init mainsvr hash mem size %d",iCountSize));
        //标准全局初始化，并设置共享内存长度
        pstShmCtrl->StartFullInit(iCountSize);
    }

    iRet = CT_CMDFACTORY->Init(pszConfig, m_stMgrShm.GetBuffer() + BufOffset, iFactorySize);
    if (iRet)
    {
        CT_ERROR(("init cmd factory error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iFactorySize;
    CT_INFO(("START......init cmd factory OK!"));

    iRet = m_stPlayerSnsCommInfoHash.Init(m_stMgrShm.GetBuffer() + BufOffset, MAX_REGISTER_PLAYER);
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerSnsCommInfoHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iPlayerSnsCommInfoSize;
    CT_INFO(("START......init player sns comm hash OK!"));

    iRet = m_stPlayerBaseInfoHash.Init(m_stMgrShm.GetBuffer() + BufOffset, USER_BASE_INFO_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerBaseInfoHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iPlayerBaseInfoSize;
    CT_INFO(("START......init player base info OK!"));

    iRet = m_stPlayerDetailInfoHash.Init(m_stMgrShm.GetBuffer() + BufOffset, USER_DETAIL_INFO_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerDetailInfoHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iPlayerDetailInfoSize;
    CT_INFO(("START......init player detail info OK!"));

    iRet = m_stPlayerDetailEx1InfoHash.Init(m_stMgrShm.GetBuffer() + BufOffset, USER_DETAIL_INFO_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerDetailEx1InfoHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iPlayerDetailEx1InfoSize;
    CT_INFO(("START......init player detail ex1 info OK!"));

    iRet = m_stUnactPartnerInfoHash.Init(m_stMgrShm.GetBuffer() + BufOffset, UNACT_PARTNER_INFO_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stUnactPartnerInfoHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iUnactPartnerInfoSize;
    CT_INFO(("START......init unact partner info OK!"));

    iRet = m_stPlayerSnsDetailInfoHash.Init(m_stMgrShm.GetBuffer() + BufOffset, USER_SNS_DETAIL_INFO_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerSnsDetailInfoHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iPlayerSnsDetailInfoSize;
    CT_INFO(("START......init player sns detail hash OK!"));

    iRet = m_stInstanceZoneDetailInfoHash.Init(m_stMgrShm.GetBuffer() + BufOffset, MAX_INSTANCE_ZONE_DETAIL_INFO_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stInstanceZoneDetailInfoHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iInstanceZoneDetailInfoSize;
    CT_INFO(("START......init instance zone detail info hash OK!"));

    iRet = m_stRolenameHash.Init(m_stMgrShm.GetBuffer() + BufOffset, MAX_REGISTER_PLAYER);
    if (iRet)
    {
        CT_ERROR(("init m_stRolenameHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iRolenameHashSize;
    CT_INFO(("START......init role name hash OK!"));

    iRet = m_stUserTokenToGID.Init(m_stMgrShm.GetBuffer() + BufOffset, USER_TOKEN_TO_GID_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stUserTokenToGID error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iUserTokenToGID;
    CT_INFO(("START......init user token hash OK!"));

    iRet = m_stPlayerOnline.Init(m_stMgrShm.GetBuffer() + BufOffset, OBJ_STAT->IsReset());
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerOnline error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iPlayerOnlineSize;
    CT_INFO(("START......init player online OK!"));

    iRet = m_stMapViews.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stMapViews error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iMapViewSize;
    CT_INFO(("START......init map view OK!"));

    iRet = m_stEquipMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stEquipMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iEquipSize;
    CT_INFO(("START......init equip mgr OK!"));

    m_stPlayerOnlineDataHash.m_bNeedConstructValue = false;
    iRet = m_stPlayerOnlineDataHash.Init(m_stMgrShm.GetBuffer() + BufOffset, MAX_PLAYER_ONLINE_DATA_COUNT);
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerOnlineDataHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += m_stPlayerOnlineDataHash.CountSize(MAX_PLAYER_ONLINE_DATA_COUNT);

    for (int i = 0; i < MAX_PLAYER_ONLINE_DATA_COUNT; ++i)
    {
        iRet = m_stPlayerOnlineDataHash[i].Init(m_stMgrShm.GetBuffer() + BufOffset);
        if (iRet != 0)
        {
            CT_ERROR(("init m_stPlayerOnlineDataHash data error"));
            CHECK_THROW(iRet);
        }
        BufOffset += CPlayerOnlineData::CountSize();
    }
    CT_INFO(("START......init player online data hash OK!"));

    iRet = m_stPlayerCacheDataHash.Init(m_stMgrShm.GetBuffer() + BufOffset, MAX_REGISTER_PLAYER);
    if (iRet)
    {
        CT_ERROR(("init m_stPlayerCacheDataHash error"));
        CHECK_THROW(iRet);
    }
    BufOffset += m_stPlayerCacheDataHash.CountSize(MAX_REGISTER_PLAYER);

    for (int i = 0; i < MAX_REGISTER_PLAYER; ++i)
    {
        iRet = m_stPlayerCacheDataHash[i].Init(m_stMgrShm.GetBuffer() + BufOffset);
        if (iRet != 0)
        {
            CT_ERROR(("init m_stPlayerCacheDataHash data error"));
            CHECK_THROW(iRet);
        }
        BufOffset += CPlayerCacheData::CountSize();
    }
    CT_INFO(("START......init player cache data hash OK!"));

    iRet = m_stItemMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stItemMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iItemMgrSize;
    CT_INFO(("START......init item mgr OK!"));

    iRet = m_stSkillMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stSkillMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iSkillMgrSize;
    CT_INFO(("START......init skill mgr OK!"));

    iRet = m_stMonsterConfigMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stMonsterConfigMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iMonsterConfigMgrSize;
    CT_INFO(("START......init monster config mgr OK!"));

    iRet = m_stInstanceMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stInstanceMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iInstanceMgrSize;
    CT_INFO(("START......init instance mgr OK!"));

    iRet = m_stDrugMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stDrugMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iDrugMgrSize;
    CT_INFO(("START......init drup mgr OK!"));

    iRet = m_stArenaMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stArenaMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iArenaMgrSize;
    CT_INFO(("START......init arena mgr OK!"));

    iRet = m_stClanMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stClanMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iClanMgrSize;
    CT_INFO(("START......init clan mgr OK!"));

    iRet = m_stStatusMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stStatusMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iStatusMgrSize;
    CT_INFO(("START......init status mgr OK!"));

    iRet = m_stActMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stActMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iActMgrSize;
    CT_INFO(("START......init act mgr OK!"));

    iRet = m_stSceneMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stSceneMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iSceneMgrSize;
    CT_INFO(("START......init scene mgr OK!"));

    iRet = m_stLessonMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stLessonMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iLessonMgrSize;
    CT_INFO(("START......init lesson mgr OK!"));

    iRet = m_stClanBattleMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stBattleMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iClanBattleMgrSize;
    CT_INFO(("START......init clan battle mgr OK!"));

    iRet = m_stAuctionMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stAuctionMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iAuctionMgrSize;
    CT_INFO(("START......init Auction mgr OK!"));

    iRet = m_stQQMallMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stQQMallMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iMallMgrSize;
    CT_INFO(("START......init Mall mgr OK!"));

    iRet = m_stCommConfigLoader.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stCommConfigLoader error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iCommConfigLoaderSize;
    CT_INFO(("START......init m_stCommConfigLoader OK!"));

    iRet = m_stAcaCupMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stAcaCupMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iAcaCupMgrSize;
    CT_INFO(("START......init m_stMgrShm OK!"));

    iRet = m_stChatMgr.Init(m_stMgrShm.GetBuffer() + BufOffset);
    if (iRet)
    {
        CT_ERROR(("init m_stChatMgr error"));
        CHECK_THROW(iRet);
    }
    BufOffset += iChatMgrSize;
    CT_INFO(("START......init m_stChatMgr OK!"));

    OBJ_STAT->SetReset();
    //初始化数据
    iRet = InitData();
    if (RET_OK != iRet)
    {
        CT_ERROR(("init data error, ret %d", iRet));
        return iRet;
    }

    pstShmCtrl->EndFullInit();

    //清理接收缓冲区
    m_stShmQueue.RemoveAllRecv();

    CT_INFO(("mainsvr START"));

    return iRet;
}

void CMainCtrl::SwapData()
{
    const float fMaxCountRate = 0.8;

    m_stPlayerBaseInfoHash.SwapHashByMaxCount(m_stPlayerBaseInfoHash.GetObjectCount() * fMaxCountRate);
    m_stPlayerDetailInfoHash.SwapHashByMaxCount(m_stPlayerDetailInfoHash.GetObjectCount() * fMaxCountRate);
    m_stUnactPartnerInfoHash.SwapHashByMaxCount(m_stUnactPartnerInfoHash.GetObjectCount() * fMaxCountRate);
    m_stPlayerSnsDetailInfoHash.SwapHashByMaxCount(m_stPlayerSnsDetailInfoHash.GetObjectCount() * fMaxCountRate);
    m_stPlayerDetailEx1InfoHash.SwapHashByMaxCount(m_stPlayerDetailEx1InfoHash.GetObjectCount() * fMaxCountRate);

    int iIndex = m_stPlayerOnlineDataHash.GetHeadIndex();
    while (1.0 * m_stPlayerOnlineDataHash.Size(m_stPlayerOnlineDataHash.USED_LIST) / m_stPlayerOnlineDataHash.GetObjectCount() >= fMaxCountRate)
    {
        if (iIndex < 0)
        {
            break;
        }

        int iNextIndex = m_stPlayerOnlineDataHash.GetNextIndex(iIndex);
        if (!m_stPlayerOnline.CheckOnline(m_stPlayerOnlineDataHash[iIndex]._uiGID))  //只换出不在线的
        {
            m_stPlayerOnlineDataHash.HashFreeByIndex(iIndex);
        }

        iIndex = iNextIndex;
    }
}

void CMainCtrl::CheckSysBusy()
{
    if (m_bIsRunSlow)
    {
        m_bIsSysBusy = true;
        return;
    }

    const float fMaxCountRate = 0.9;

    if (CT_CMDFACTORY->IsBusy(fMaxCountRate * 100)
        ||m_stUpdateMgr.CheckPassUsedPentage(fMaxCountRate * 100)
        ||m_stPlayerBaseInfoHash.Size(m_stPlayerBaseInfoHash.USED_LIST) >= m_stPlayerBaseInfoHash.GetObjectCount() * fMaxCountRate
        ||m_stPlayerDetailInfoHash.Size(m_stPlayerDetailInfoHash.USED_LIST) >= m_stPlayerDetailInfoHash.GetObjectCount() * fMaxCountRate
        ||m_stUnactPartnerInfoHash.Size(m_stUnactPartnerInfoHash.USED_LIST) >= m_stUnactPartnerInfoHash.GetObjectCount() * fMaxCountRate
        ||m_stPlayerSnsDetailInfoHash.Size(m_stPlayerSnsDetailInfoHash.USED_LIST) >= m_stPlayerSnsDetailInfoHash.GetObjectCount() * fMaxCountRate
        ||m_stPlayerOnlineDataHash.Size(m_stPlayerOnlineDataHash.USED_LIST) >= m_stPlayerOnlineDataHash.GetObjectCount() * fMaxCountRate
        ||m_stPlayerDetailEx1InfoHash.Size(m_stPlayerDetailEx1InfoHash.USED_LIST) >= m_stPlayerDetailEx1InfoHash.GetObjectCount() * fMaxCountRate)
    {
        m_bIsSysBusy = true;
        return;
    }

    m_bIsSysBusy = false;
}

int CMainCtrl::Run()
{
    int iRet = 0;

    int iLastTimeTickTime = time(NULL);  //上次秒触发时间
    int iLastRunTime = time(NULL);       //上次主循环的时间

    time_t tTimeNow = 0;
    struct tm* pstTm = NULL;

    m_bBeginRun = true;

    while (true)
    {
        gettimeofday(&m_stNowTime, NULL);

#ifdef _DEBUG
        m_iNow = m_stNowTime.tv_sec + m_stZoneOnlineData._stZoneData._iTimeOffSet;
#else
        m_iNow = m_stNowTime.tv_sec;
#endif

        //确保每秒都会进入主循环一次
        if (iLastRunTime + 1 < m_iNow && iLastRunTime + 30 > m_iNow)
        {
            m_iNow = iLastRunTime + 1;
            iLastRunTime++;
            CT_WARNING(("mainctrl run slow, sec %d", m_iNow - iLastRunTime));
        }
        else
        {
            iLastRunTime = m_iNow;
        }

        if (s_bExit)
        {
            return 0;
        }

        if (s_bReloadConfig)
        {
            s_bReloadConfig = false;
            iRet = CONF->ReloadConfig();
            if (iRet)
            {
                CT_ERROR(("reload sys config Err !!!"));
                return 0;
            }

            m_stUpdateMgr.ReSetUpdateFlushNum(CONF->InitDataFlushNum,CONF->MaxDataFlushNum);
        }

        m_stStat.Put(main_stat_mainloop);

        iRet = m_stEpoll.WaitAndEvent(10);
        if (iRet == 0) // 超时了
        {
            m_stStat.Put(main_stat_waittimeout);
        }

        //检查数据刷新
        bool bIsSwap = false;
        m_stUpdateMgr.CheckUpdate(1,m_iNow);
        m_stUpdateMgr.CheckSwap(1, m_iNow, 10, &bIsSwap);
        //检查数据刷新结束

        // 检查连接是否断开
        m_stDB.CheckConnect();
        m_stDB.CheckSend();
        m_stApi.CheckConnect();
        m_stApi.CheckSend();

        m_stMemoryDB.CheckSend();

        HandleShmQueueMsg();

        //换出数据, 主要是hash
        SwapData();

        //检查系统繁忙
        CheckSysBusy();

        if (m_iNow != iLastTimeTickTime)
        {
            //触发跨天的处理,每天凌晨的时候执行一次
            if (false == CTTimeUtils::IsSameDay(m_iNow, iLastTimeTickTime, 0))
            {
                ProcWhenNewDayBegin(m_iNow);
            }

            iLastTimeTickTime = m_iNow;

            tTimeNow = m_iNow;
            pstTm = localtime((time_t*)&tTimeNow);
            if (pstTm)
            {
                m_stTm = *pstTm;
            }
            m_iDayZeroTime = CTLib::CTTimeUtils::GetDayBeginSec(m_iNow);

            //输出统计信息
            if (m_iNow % 15 == 0)
            {
                DumpStatInfo();
            }

            //秒级操作
            CT_CMDFACTORY->CheckTimeoutCmd(m_iNow);

            //每秒更新活动状态
            m_stActMgr.RefreshAct();

            //检查防沉迷
            //CheckDef();

            //检查家族是否需要自荐和解散
            m_stClanMgr.TimeTick(m_iNow);

            //家族神树结算
            m_stTreeMgr.TimeTick(m_iNow);

            //竞技场结算
            m_stArenaMgr.SecTick(m_iNow);

            //怪物攻城战
            m_stEnemyAtkCityMgr.TimeTick(m_iNow);

            //OSS系统
            m_stOssMgr.TimeTick(m_iNow);

            //学院排行
            m_stAcademyMgr.TimeTick(m_iNow);

            //拍卖会
            m_stAuctionMgr.TimeTick (m_iNow);

            //精彩活动
            m_stActMgr.SplendidActSecTick(m_iNow);

            //家族战
            m_stClanBattleMgr.TimeTick(m_iNow);

            //排行榜
            m_stRankMgr.TimeTick(m_iNow);

            //学院杯
            m_stAcaCupMgr.ActStatChange(m_iNow);

            //腾讯帐号保活
            const int KEEP_TENCENT_LOGIN_SEC = 900;
            if (((m_iNow + 123) % KEEP_TENCENT_LOGIN_SEC) == 0)
            {
                TencentKeepLoginValid(KEEP_TENCENT_LOGIN_SEC);
            }
        }

        if (!m_bIsSysBusy)
        {
            // 处理异步命令队列中命令
            CT_CMDFACTORY->DoQueue();
        }
    }

    return 0;
}

int CMainCtrl::Exit()
{
    CTArray<unsigned int, MAX_PLAYER_ONLINE> astGIDArray;
    m_stPlayerOnline.GetAllOnlineUser(astGIDArray);
    for (int i = 0; i < astGIDArray.Size(); ++i)
    {
        //踢出所有用户
        CEnpNetHead stHead;
        m_stPlayerOnline.GetOnlinePlayerHandle(astGIDArray[i], stHead);
        SetOffline(astGIDArray[i], stHead);

        m_stEpoll.WaitAndEvent(10);

#if 0
        //热更时,触发OSS的结算
        m_stOssMgr.TriggerExit(astGIDArray[i], m_iNow);
#endif
    }

    //执行完所有命令队列
    while(CT_CMDFACTORY->DoQueue())
    {
        m_stEpoll.WaitAndEvent(10);
    }

    //刷新OSS每隔十五分钟的统计
    m_stOssMgr.UpdateZoneData(m_iNow);

    //刷新数据, 只有数据刷完才停机
    m_stUpdateMgr.ReSetUpdateFlushNum(CONF->InitDataFlushNum, CONF->MaxDataFlushNum);

    while(!m_stUpdateMgr.CheckEmpty())
    {
        //CMainCtrl::Run已经退出了, 所以不能使用 m_iNow
#ifdef _DEBUG
        int iNowTime = time(NULL) + m_stZoneOnlineData._stZoneData._iTimeOffSet;
#else
        int iNowTime = time(NULL);
#endif
        m_stUpdateMgr.CheckUpdate(1,iNowTime);

        bool bSwapMgr = false;
        m_stUpdateMgr.CheckSwap(2,iNowTime,20, &bSwapMgr);
        if (bSwapMgr)
        {
            CT_UPDATE(("--------------------swap--------------------"));
        }
        m_stEpoll.WaitAndEvent(1000);
    }

    CT_INFO(("%s exit", APPNAME));
    return 0;
}

void CMainCtrl::OnShmQueueEvent(CMainEpollObject* pstObject, SOCKET iSocket, int iEvent)
{
    CT_TRACE(("OnShmQueueEvent socket=%d event=%d", iSocket, iEvent));
    m_stShmQueue.ClearNotify(); // 接收Notify数据, 否则会一直收到事件
    //TODO: 每个消息都要执行ClearNotify, 可能存在性能问题, 以后验证

    if (!m_bBeginRun)
    {
        CT_WARNING(("mainsvr is initing"));
        return ;
    }

    if (!CTEpoll::IsInputEvent(iEvent))
    {
        CT_WARNING(("ShmQueue recv not match event(%d) socket=%d", iEvent, iSocket));
        return;
    }

    HandleShmQueueMsg();
}

void CMainCtrl::DumpStatInfo()
{
    m_stStat.Dump(CT_STAT);
    m_stCmdStat.Dump2(CT_STAT);

    CT_STAT->Log(EInfo, "CPlayerBaseInfo: All=%d Used=%d Free=%d",
        m_stPlayerBaseInfoHash.GetObjectCount(),
        m_stPlayerBaseInfoHash.Size(1),
        m_stPlayerBaseInfoHash.Size(0));

    CT_STAT->Log(EInfo, "CPlayerDetailInfo: All=%d Used=%d Free=%d",
        m_stPlayerDetailInfoHash.GetObjectCount(),
        m_stPlayerDetailInfoHash.Size(1),
        m_stPlayerDetailInfoHash.Size(0));

    CT_STAT->Log(EInfo, "CPlayerSnsDetailInfo: All=%d Used=%d Free=%d",
        m_stPlayerSnsDetailInfoHash.GetObjectCount(),
        m_stPlayerSnsDetailInfoHash.Size(1),
        m_stPlayerSnsDetailInfoHash.Size(0));

    CT_STAT->Log(EInfo, "CPlayerSnsCommInfo: All=%d Used=%d Free=%d",
        m_stPlayerSnsCommInfoHash.GetObjectCount(),
        m_stPlayerSnsCommInfoHash.Size(1),
        m_stPlayerSnsCommInfoHash.Size(0));

    CT_STAT->Log(EInfo, "CUnactPartnerInfo: All=%d Used=%d Free=%d",
        m_stUnactPartnerInfoHash.GetObjectCount(),
        m_stUnactPartnerInfoHash.Size(1),
        m_stUnactPartnerInfoHash.Size(0));

    CT_STAT->Log(EInfo, "CPlayerOnlineDataHash: All=%d Used=%d Free=%d",
        m_stPlayerOnlineDataHash.GetObjectCount(),
        m_stPlayerOnlineDataHash.Size(1),
        m_stPlayerOnlineDataHash.Size(0));

    CT_CMDFACTORY->DumpStatDetail(CT_STAT);
    CT_STAT->Log(EInfo, "%s",m_stUpdateMgr.GetStatInfo());

    CT_STAT->Log(EInfo, "Online: %d", m_pstMain->m_stPlayerOnline.GetOnlineCount());

    CT_CMDFACTORY->DumpStatInfo(CT_STAT);

    //建造等级信息统计
    /*
    CT_STAT->Log(EInfo, "BuildingRang: All=%d Used=%d Free=%d",
    m_stBuildingRankHash.GetObjectCount(),
    m_stBuildingRankHash.Size(CTHashObjectMgr<unsigned int, CBuildingRank>::USED_LIST),
    m_stBuildingRankHash.Size(CTHashObjectMgr<unsigned int, CBuildingRank>::FREE_LIST));
    */
}

void CMainCtrl::DumpPBMsgToFile(const char* pszKey, ::google::protobuf::Message& pbData)
{
    if (!pszKey)
    {
        return;
    }

    std::fstream stFileOutput(pszKey,
        std::ios::out | std::ios::trunc | std::ios::binary);
    pbData.SerializeToOstream(&stFileOutput);
    stFileOutput.close();
}

void CMainCtrl::HandleShmQueueMsg()
{
    int iRet = 0;
    CTCodeStream s;
    PBMsgHead stMsgHead;

    for (int i = 0; i < 100; i++) //此处的100为性能测试时通过反复调整获得的值
    {
        if (!m_stShmQueue.HasCode())
        {
            return;
        }

        // 接收消息
        int iCodeLen = 0;
        m_stDecodeBuf.Clear();
        iRet = m_stShmQueue.GetOneCode(m_stDecodeBuf.GetFreeBuf(),
            m_stDecodeBuf.GetFreeLen(),
            iCodeLen);
        if (iRet || iCodeLen < (int)sizeof(CEnpNetHead))
        {
            CT_WARNING(("get code from shmqueue failed ret=%d, codelen=%d",
                iRet, iCodeLen));
            continue;
        }
        m_stDecodeBuf.Append(iCodeLen);

        // 先获取NetHead
        CEnpNetHead& stHead = *(CEnpNetHead*)m_stDecodeBuf.GetUsedBuf();
        if (iCodeLen != (int)sizeof(CEnpNetHead) + stHead.unDataLength) // 长度不一致
        {
            CT_WARNING(("code length invalid. %d != %d + %d", iCodeLen,
                sizeof(CEnpNetHead), stHead.unDataLength));
            continue;
        }

        // 判断数据长度
        if (stHead.unDataLength <= sizeof(int)) //连接断开
        {
            if (stHead.uiAct1 != 0) //uiAct保存了用户uin
            {
                CEnpNetHead stHeadTemp;
                m_stPlayerOnline.GetOnlinePlayerHandle(stHead.uiAct1, stHeadTemp);

                //使用相同的handle才可以设置下线状态
                if (stHeadTemp.uiHandle != stHead.uiHandle)
                {
                    CT_WARNING(("find invalid disconnect, uin = %d, handle = %d",
                        stHead.uiAct1, stHead.uiHandle));
                    continue;
                }

                SetOffline(stHead.uiAct1, stHead);

                CT_TRACE(("%d disconnected", stHead.uiAct1));
            }
            else
            {
                CT_WARNING(("find client disconnect, but uin is invalid, handle is %u",
                    stHead.uiHandle));
            }
            continue;
        }

        if (CODE_HTTP == stHead.ucDeodeMethod)
        {
            HandleHttpMsg(stHead, m_stDecodeBuf.GetUsedBuf() + sizeof(CEnpNetHead), stHead.unDataLength);
            continue;
        }

        // 再获取MsgHead
        int iMsgHeadLen = ntohl(*(int*)(m_stDecodeBuf.GetUsedBuf() +
            sizeof(CEnpNetHead)));
        if (iMsgHeadLen <= 0)
        {
            CT_ERROR(("msghead buff is empty(%d)!", iMsgHeadLen));
            continue;
        }

        CT_TRACE(("iMsgHeadLen = %d", iMsgHeadLen));

        if (iMsgHeadLen > stHead.unDataLength - (int)sizeof(int))
        {
            CT_ERROR(("msghead buff length (%d) bigger than data length(%d)!",
                iMsgHeadLen, stHead.unDataLength));
            continue;
        }

        if (DecodePBMsg(stMsgHead,(void*)(m_stDecodeBuf.GetUsedBuf()+sizeof(CEnpNetHead)+sizeof(int)),iMsgHeadLen))
        {
            CT_ERROR(("parse msghead from buff error!"));
            continue;
        }

        stMsgHead.set_time(m_iNow);

        CT_TRACE(("version=%d time=%d msgact=%d msgtype=%d msgid=%d",
            stMsgHead.version(),stMsgHead.time(),stMsgHead.msgact(),
            stMsgHead.msgtype(),stMsgHead.msgid()));

        // 再处理MsgBody
        if (stHead.unDataLength <= sizeof(int) + iMsgHeadLen + sizeof(int))
        {
            CT_WARNING(("msgbody buff is empty!"));
            continue;
        }
        int iMsgBodyLen = ntohl(*(int*)(m_stDecodeBuf.GetUsedBuf() +
            sizeof(CEnpNetHead) + sizeof(int) + iMsgHeadLen));
        if (iMsgBodyLen <= 0)
        {
            CT_WARNING(("msgbody buff is empty(%d)!", iMsgBodyLen));
            continue;
        }

        CT_TRACE(("iMsgBodyLen = %d", iMsgBodyLen));

        if (iMsgBodyLen > stHead.unDataLength - (int)sizeof(int) - iMsgHeadLen - (int)sizeof(int))
        {
            CT_ERROR(("msgbody buff length (%d) bigger than data length(%d)!",
                iMsgBodyLen, stHead.unDataLength - iMsgHeadLen));
            continue;
        }

        // 这里都是处理来自client的请求
        iRet = s.Attach(m_stDecodeBuf.GetUsedBuf() + sizeof(CEnpNetHead) +
            sizeof(int) + iMsgHeadLen + sizeof(int), iMsgBodyLen);
        CHECK_THROW(iRet);
#ifndef _DEBUG
        // 安全检查: 有些协议虽然合法, 但可能是内部协议, 不允许从client发过来
        if (FromAdmin(stHead) == false)
        {
            if (!AuthClientMsg(stMsgHead)) // 从客户端来的消息
            {
                CT_WARNING(("receive from client, but is not client msg"));
                continue;
            }
        }
        else
        {
            ; //管理端的命令都可以处理
        }
#endif
        //加一个命令和数据上限检查，并且给用户服务端繁忙的响应
        if (m_bIsSysBusy)
        {
            stMsgHead.set_msgret(RET_SYS_BUSY);
            ReturnMsgHead(stMsgHead, stHead);
            CT_ERROR(("system is busy msgid %d", stMsgHead.msgid()));
            continue;
        }

        //CT_TRACE(("CCmdMineCreateArmy size is %d", sizeof(CCmdMineCreateArmy)));
        // 创建异步命令
        CTAsyncCmd* pstCmd = (CTAsyncCmd*) CT_CMDFACTORY->CreateCmd(stMsgHead.msgid());
        if (!pstCmd)
        {
            CT_WARNING((""));
            continue;
        }

        // 异步命令执行完后能自己释放
        CCmdPBDoParam stParam((void*)this, stHead, stMsgHead, s);

#ifdef _DEBUG
        struct timeval stCmdTVBegin;
        struct timeval stCmdTVEnd;

        gettimeofday(&stCmdTVBegin, NULL);
        iRet = pstCmd->Do(&stParam);
        gettimeofday(&stCmdTVEnd, NULL);

        int iRunMicroSec = (stCmdTVEnd.tv_sec - stCmdTVBegin.tv_sec) * 1000
            + (stCmdTVEnd.tv_usec - stCmdTVBegin.tv_usec) / 1000;

        if (pstCmd->IsValid())
        {
            pstCmd->AddRunMicroSec(iRunMicroSec);
        }
        else
        {
            m_stCmdStat.Put2(stMsgHead.msgid(), iRunMicroSec);
        }

#else
        iRet = pstCmd->Do(&stParam);
#endif

        //在这里需要判断是否为低端错误如果是则应该将其踢掉线
        CT_TRACE((""));
    }
}

bool CMainCtrl::AuthClientMsg(const PBMsgHead& stMsgHead) const
{
    return (stMsgHead.msgtype() == CSMsgDef::EMT_REQUEST &&
        (stMsgHead.msgid() >= ID_MAIN_MIN &&
        stMsgHead.msgid() <= ID_MAIN_MAX));
}

bool CMainCtrl::FromAdmin(const CEnpNetHead& stHead)
{
    return (stHead.uiLocalIP == inet_addr(CONF->AdminHost.Get()) &&
        (stHead.unLocalPort == CONF->AdminPort));
}

int CMainCtrl::AnswerToClient(CEnpNetHead& stHead, CTBuffer& buff, bool bFlushBuf)
{
    int iRet = 0;

    if (NULL == m_szSendCache)
    {
        int iBuffSize = CONF->FrontEndShmSize / 2;
        m_szSendCache = new char[iBuffSize];
        m_stSendCache.Attach(m_szSendCache, iBuffSize, 0);
    }

    if (m_stSendCache.GetFreeLen() < (int)sizeof(stHead) + buff.GetUsedLen())
    {
        CT_ERROR(("buff full"));
        return RET_SYS_ERROR;
    }

    if (!bFlushBuf)
    {
        stHead.unDataLength = buff.GetUsedLen();

        *(int*)(m_stSendCache.GetFreeBuf()) = sizeof(stHead) + buff.GetUsedLen();
        m_stSendCache.Append(sizeof(int));

        memcpy(m_stSendCache.GetFreeBuf(), (const char*)&stHead, sizeof(stHead));
        memcpy(m_stSendCache.GetFreeBuf() + sizeof(stHead), buff.GetUsedBuf(), buff.GetUsedLen());

        //#ifdef _DEBUG
        CT_MESSAGE(("[Append To List][%u]",GetPkgKey()));
        //#endif
        m_stSendCache.Append(sizeof(stHead) + buff.GetUsedLen());
    }
    else
    {
        //#ifdef _DEBUG
        CT_MESSAGE(("[Flush The List][%u]",GetPkgKey()));
        //#endif
        stHead.unDataLength = buff.GetUsedLen();

        iRet = m_stShmQueue.PutOneCode((const char*)&stHead, sizeof(stHead), buff.GetUsedBuf(), buff.GetUsedLen());
        if (iRet != RET_OK)
        {
            CT_WARNING((""));
        }

        FlushSendCache();
    }

    return iRet;
}

void CMainCtrl::FlushSendCache()
{
    //发送缓存的数据
    int iOffSet = 0;
    while (m_stSendCache.GetUsedLen() > iOffSet)
    {
        int iLen = *(int*)(m_stSendCache.GetUsedBuf() + iOffSet);
        iOffSet += sizeof(int);

        m_stShmQueue.PutOneCode(m_stSendCache.GetUsedBuf() + iOffSet, iLen);
        iOffSet += iLen;
    }

    m_stSendCache.Clear();
}

//更新数据
int CMainCtrl::UpdateData(CVarObject* pstObj, CVarObjectKey* pstKey)
{
    if(pstObj == NULL || pstKey == NULL)
    {
        return -1;
    }

    CT_TRACE(("UPDATE_DATA: begin flush data to hash , key1 = %s, key2 = %llu, dt = %d, op = %d datalen %d swapcount %d",
        pstKey->m_sStrID(), pstKey->m_u64ID, pstKey->m_chDataType, pstObj->m_stDataHead.m_iDataInfo,
        pstObj->m_stDataHead.m_iDataLen, CMainCtrl::m_pstMain->m_stUpdateMgr.GetSwapCount()));

    CCmdUpdateData *pstCmd = (CCmdUpdateData*)CT_CMDFACTORY->CreateCmd(ID_MAINDB_UPDATEDATA);
    if (!pstCmd)
    {
        return -1;
    }
    pstCmd->m_pstMain = CMainCtrl::m_pstMain;
    pstCmd->m_stReq.pszData    = pstObj->m_chData;
    pstCmd->m_stReq.iDataLen   = pstObj->m_stDataHead.m_iDataLen;
    pstCmd->m_stReq.m_stKey    = *pstKey;
    pstCmd->m_stReq.m_iOpType  = pstObj->m_stDataHead.m_iDataInfo;
    pstCmd->m_stReq.m_uiSwapCount = CMainCtrl::m_pstMain->m_stUpdateMgr.GetSwapCount();

#ifdef _DEBUG
    struct timeval stCmdTVBegin;
    struct timeval stCmdTVEnd;

    gettimeofday(&stCmdTVBegin, NULL);
    int iRet = pstCmd->Do(NULL);
    gettimeofday(&stCmdTVEnd, NULL);

    int iRunMicroSec = (stCmdTVEnd.tv_sec - stCmdTVBegin.tv_sec) * 1000
        + (stCmdTVEnd.tv_usec - stCmdTVBegin.tv_usec) / 1000;

    if (pstCmd->IsValid())
    {
        pstCmd->AddRunMicroSec(iRunMicroSec);
    }
    else
    {
        CMainCtrl::m_pstMain->m_stCmdStat.Put2(ID_MAINDB_UPDATEDATA, iRunMicroSec);
    }

#else
    int iRet = pstCmd->Do(NULL);
#endif

    if (iRet != 0)
    {
        CT_WARNING(("CCmdUpdateData do() error"));
        return -1;
    }
    CT_TRACE((""));

    return 0;
}

void CMainCtrl::HandleDBMsg(char* pszMsgBuf, int iMsgLen)
{
    if (!pszMsgBuf || iMsgLen > CTMsgHead::EMAX_MSGLEN)
    {
        CT_WARNING((""));
        return;
    }

    int iRet = 0;
    CTCodeStream s(pszMsgBuf, (unsigned short)(iMsgLen & 0x7FFF));

    // 解析消息头部
    CTMsgHead stMsgHead;
    s.InitConvert();
    iRet = CodeConvert(s, stMsgHead, NULL, bin_decode());
    CT_TRACE(("Get DB AnsMsg ID=%d ", stMsgHead.shMsgID));
    if (iRet)
    {
        CT_WARNING((""));
        return;
    }

    if (!stMsgHead.IsValid())
    {
        CT_WARNING((""));
        return;
    }

    // 根据ACT找到对应发出请求的异步命令
    CTAsyncCmdInf* pstCmd = reinterpret_cast<CTAsyncCmdInf*>(stMsgHead.llMsgAct);
    if (!CT_CMDFACTORY->IsValidCmd(pstCmd))
    {
        CT_WARNING((""));
        return;
    }

    // 检查消息的Seq是否匹配
    if (pstCmd->GetCmdSeq() != stMsgHead.iCmdSeq)
    {
        CT_WARNING((""));
        return;
    }

    // 执行响应函数
    CCmdOnAnswerParam stParam((void*)this, stMsgHead, s);

#ifdef _DEBUG
    struct timeval stCmdTVBegin;
    struct timeval stCmdTVEnd;

    int iRunMicroSec = pstCmd->GetRunMicroSec();
    int iCmdID = pstCmd->GetCmdID();

    gettimeofday(&stCmdTVBegin, NULL);
    iRet = pstCmd->OnAnswer(&stParam);
    gettimeofday(&stCmdTVEnd, NULL);

    iRunMicroSec += (stCmdTVEnd.tv_sec - stCmdTVBegin.tv_sec) * 1000
        + (stCmdTVEnd.tv_usec - stCmdTVBegin.tv_usec) / 1000;

    if (pstCmd->IsValid())
    {
        pstCmd->AddRunMicroSec(iRunMicroSec);
    }
    else
    {
        m_stCmdStat.Put2(iCmdID, iRunMicroSec);
    }

#else
    iRet = pstCmd->OnAnswer(&stParam);
#endif

    CT_TRACE((""));
}

/**
* 向dbsvr发送请求
*/
int CMainCtrl::RequestToDB(const char* pszBuf, int iBufLen)
{
    static char sSendBuf[65536];

    unsigned int uiLen = (unsigned int)((iBufLen + sizeof(unsigned int)) & 0xFFFF);
    uiLen = htonl(uiLen);

    memcpy(sSendBuf, &uiLen, sizeof(unsigned int));
    memcpy(sSendBuf + sizeof(unsigned int), pszBuf, iBufLen);

    return m_stDB.Send(sSendBuf, sizeof(unsigned int) + iBufLen);
}

void CMainCtrl::HandleApiMsg(char* pszMsgBuf, int iMsgLen)
{
    if (!pszMsgBuf || iMsgLen > CTMsgHead::EMAX_MSGLEN)
    {
        CT_WARNING((""));
        return;
    }

    int iRet = 0;
    CTCodeStream s(pszMsgBuf, (unsigned short)(iMsgLen & 0x7FFF));

    // 解析消息头部
    CTMsgHead stMsgHead;
    s.InitConvert();
    iRet = CodeConvert(s, stMsgHead, NULL, bin_decode());
    CT_TRACE(("Get Api AnsMsg ID=%d ", stMsgHead.shMsgID));
    if (iRet)
    {
        CT_WARNING((""));
        return;
    }

    if (!stMsgHead.IsValid())
    {
        CT_WARNING((""));
        return;
    }

    //命令返回
    if (stMsgHead.llMsgAct != 0)
    {
        // 根据ACT找到对应发出请求的异步命令
        CTAsyncCmdInf* pstCmd = reinterpret_cast<CTAsyncCmdInf*>(stMsgHead.llMsgAct);
        if (!CT_CMDFACTORY->IsValidCmd(pstCmd))
        {
            CT_WARNING((""));
            return;
        }

        // 检查消息的Seq是否匹配
        if (pstCmd->GetCmdSeq() != stMsgHead.iCmdSeq)
        {
            CT_WARNING((""));
            return;
        }

        // 执行响应函数
        CCmdOnAnswerParam stParam((void*)this, stMsgHead, s);
        iRet = pstCmd->OnAnswer(&stParam);
    }
    else    //新请求
    {
        CTAsyncCmd* pstCmd = (CTAsyncCmd*) CT_CMDFACTORY->CreateCmd(stMsgHead.shMsgID);
        if (!pstCmd)
        {
            CT_ERROR(("%d", stMsgHead.shMsgID));
            return;
        }

        // 异步命令执行完后能自己释放
        CEnpNetHead stEnpHead;
        CCmdDoParam stParam((void*)this, stEnpHead, stMsgHead, s);
        iRet = pstCmd->Do(&stParam);

        CT_TRACE((""));
    }

    CT_TRACE((""));
}

/**
* 向apisvr发送请求
*/
int CMainCtrl::RequestToApi(const char* pszBuf, int iBufLen)
{
    static char sSendBuf[65536];

    unsigned int uiLen = (unsigned int)((iBufLen + sizeof(unsigned int)) & 0xFFFF);
    uiLen = htonl(uiLen);

    memcpy(sSendBuf, &uiLen, sizeof(unsigned int));
    memcpy(sSendBuf + sizeof(unsigned int), pszBuf, iBufLen);

    return m_stApi.Send(sSendBuf, sizeof(unsigned int) + iBufLen);
}

int CMainCtrl::RequestToMemoryDBWithPB(const char* pszKey, int iKeyLen, ::google::protobuf::Message& pbData, int iExestSecs)
{
    string strResult;

    pbData.SerializeToString( &strResult );

    //DumpPBMsgToFile(pszKey, pbData);

    return this->RequestToMemoryDB( pszKey, iKeyLen, strResult.c_str(), strResult.length(), iExestSecs);
}

int CMainCtrl::BattleToMemoryDBWithPB(T_VIDEO_KEY& szVideoKey, unsigned int uiGID, int iVideoType, MsgBattleResult& stMsgBattleRes, int iExestSecs)
{
    string strResult;
    CBattleMgr::GetBattleVideoKey(uiGID, iVideoType, szVideoKey);
    stMsgBattleRes.set_fight_id(szVideoKey.Get());
    stMsgBattleRes.set_actual_battle(0);//应前端要求设置录像标志
    stMsgBattleRes.SerializeToString( &strResult );
    stMsgBattleRes.set_actual_battle(1);//恢复战斗结果为实时战斗

    return this->RequestToMemoryDB( szVideoKey(), szVideoKey.GetDataLen(), strResult.c_str(), strResult.length(), iExestSecs);
}

int CMainCtrl::RequestToMemoryDB(const char* pszKey, int iKeyLen, const char* pszValue, int iValueLen, int iExestSecs)
{
    int iRet = 0;

    iRet = this->m_stMemoryDB.Send(pszKey, pszValue, iValueLen, iExestSecs);
    CT_TRACE(("RequestToMemoryDB key = %s", pszKey));

    return iRet;
}

int CMainCtrl::CreatePlayerBaseInfo(CPlayerBaseInfo& stPlayerBaseInfo)
{
    CVarObjectKey stKey;
    stKey.m_u64ID = stPlayerBaseInfo._uiGID;
    stKey.m_chDataType = DT_PLAYER_BASE_INFO;
    stKey.m_chDataType = VOP_CREATE;

    UpdateData(stKey, stPlayerBaseInfo, VOP_CREATE);
    return 0;
}

int CMainCtrl::UpdatePlayerBaseInfo(CPlayerBaseInfo& stPlayerBaseInfo)
{
    CVarObjectKey stKey;
    stKey.m_u64ID = stPlayerBaseInfo._uiGID;
    stKey.m_chDataType = DT_PLAYER_BASE_INFO;
    stKey.m_chDataType = VOP_UPDATE;

    UpdateData(stKey, stPlayerBaseInfo, VOP_UPDATE);
    return 0;
}

int CMainCtrl::ReturnMsgHead(PBMsgHead& stMsgHead, CTLib::CEnpNetHead& stEnpHead)
{
    stMsgHead.set_version(0);
    stMsgHead.set_time(m_iNow);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);

    m_stEncodeBuf.Clear();
    EncodePBMsg(stMsgHead, m_stEncodeBuf);

    if (AnswerToClient(stEnpHead, m_stEncodeBuf, true))
    {
        CT_WARNING((""));
        return RET_SYS_ERROR;
    }

    return 0;
}

int CMainCtrl::ReturnMsgHead(unsigned int uiGID, int iCmd, int iRetValue)
{
    int iRet = 0;

    CEnpNetHead stEnpHead;
    iRet = m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stEnpHead);
    if (iRet)
    {
        CT_WARNING(("online user(%u) handle not find deal cmd(%d) retvalue(%d)",
            uiGID, iCmd, iRetValue));
        return iRet;
    }

    m_stEncodeBuf.Clear();

    PBMsgHead stMsgHead;

    stMsgHead.set_version(0);
    stMsgHead.set_time(m_iNow);
    stMsgHead.set_msgid(iCmd);
    stMsgHead.set_msgret(iRetValue);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);

    EncodePBMsg(stMsgHead, m_stEncodeBuf);

    iRet = AnswerToClient(stEnpHead, m_stEncodeBuf, false);
    if (iRet)
    {
        CT_WARNING(("AnswerToClient Gid(%u) Cmd(%d) RetVal(%d) error(%d)",
            uiGID, iCmd, iRetValue, iRet));
        return iRet;
    }

    return 0;
}

int CMainCtrl::DecodePBMsg(::google::protobuf::Message& stMsgBody, void* pData, int iLength)
{
    CT_ASSERT(pData);

    if (!stMsgBody.ParseFromArray((void*)pData, iLength))
    {
        CT_WARNING(("%s %s",stMsgBody.GetTypeName().c_str(),stMsgBody.InitializationErrorString().c_str()));
        return RET_SYS_PB_DECODE_FAIL;
    }

#ifdef _DEBUG
    if (!stMsgBody.IsInitialized())
    {
        CT_WARNING(("[%ld %ld]%s %s",m_stNowTime.tv_sec,m_stNowTime.tv_usec,stMsgBody.GetTypeName().c_str(),
            stMsgBody.InitializationErrorString().c_str()));
    }
#endif
    CT_MESSAGE(("[C-->S][%s]\n%s",stMsgBody.GetTypeName().c_str(),stMsgBody.Utf8DebugString().c_str()));
    //#endif

    return 0;
}

int CMainCtrl::DecodePBMsg(::google::protobuf::Message& stMsgBody, CCmdPBDoParam* pstParam)
{
    CT_ASSERT(pstParam);

    if (!stMsgBody.ParseFromArray((void*)pstParam->m_pstCode->m_pszBuf, pstParam->m_pstCode->m_ushBufSize))
    {
        CT_WARNING(("%s %s",stMsgBody.GetTypeName().c_str(),stMsgBody.InitializationErrorString().c_str()));
        return RET_SYS_PB_DECODE_FAIL;
    }

#ifdef _DEBUG
    if (!stMsgBody.IsInitialized())
    {
        CT_WARNING(("[%ld %ld]%s %s",m_stNowTime.tv_sec,m_stNowTime.tv_usec,stMsgBody.GetTypeName().c_str(),
            stMsgBody.InitializationErrorString().c_str()));
    }
#endif
    CT_MESSAGE(("[C-->S][%s]\n%s",stMsgBody.GetTypeName().c_str(),stMsgBody.Utf8DebugString().c_str()));
    //#endif

    return 0;
}

int CMainCtrl::EncodePBMsg(::google::protobuf::Message& stMsgBody, CTBuffer& stBuff, bool bEncodeLen)
{
    int iRet = 0;

    if (stBuff.GetFreeLen() < (int)sizeof(int) + stMsgBody.ByteSize() + 1)
    {
        CT_ERROR(("msg too large"));
        return RET_SYS_ERROR;
    }

    if (bEncodeLen)
    {
        *(int*)(stBuff.GetFreeBuf()) = (int)htonl((int)(stMsgBody.ByteSize()));
        iRet = stBuff.Append(sizeof(int));
    }

#ifdef _DEBUG
    if (!stMsgBody.IsInitialized())
    {
        CT_WARNING(("[%ld %ld]%s %s",m_stNowTime.tv_sec,m_stNowTime.tv_usec,stMsgBody.GetTypeName().c_str(),
            stMsgBody.InitializationErrorString().c_str()));
    }
#endif

    if (!stMsgBody.SerializeToArray((void*)stBuff.GetFreeBuf(), stMsgBody.ByteSize()))
    {
        CT_WARNING(("%s %s",stMsgBody.GetTypeName().c_str(),stMsgBody.InitializationErrorString().c_str()));
        return RET_SYS_PB_ENCODE_FAIL;
    }
    iRet = stBuff.Append(stMsgBody.ByteSize());

    if (iRet != RET_OK)
    {
        CT_ERROR(("so large msg"));
    }

    //#ifdef _DEBUG
    AddPkgKey();
    CT_MESSAGE(("[S-->C][%s][%u]\n%s",stMsgBody.GetTypeName().c_str(),GetPkgKey(),stMsgBody.Utf8DebugString().c_str()));
    //#endif

    return iRet;
}

int CMainCtrl::InitData()
{
    int iRet = 0;

    //初始化任务配置
    iRet = m_stTaskMgr.Init();
    CHECK_RETURN(iRet);

    iRet = m_stTaskMgr.LoadConfig(CONF->TaskConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init task mgr OK!"));

    //初始化日常任务配置
    iRet = m_stDailyTaskMgr.Init();
    CHECK_RETURN(iRet);
    iRet = m_stDailyTaskMgr.LoadConfig(CONF->DailyTaskConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init dailytask mgr OK!"));

    //初始化装备配置
    iRet = m_stEquipMgr.LoadConfig(CONF->EquipConfigFilePath(), CONF->PotentialConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init equip mgr OK!"));

    //初始化物品配置(必须放在副本表的前面初始化)
    iRet = m_stItemMgr.LoadConfig(CONF->ItemConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init item mgr OK!"));

    //用户商店配置
    iRet = m_stShopMgr.LoadConfig(CONF->ShopConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init shop mgr OK!"));

    //初始化技能配置
    iRet = m_stSkillMgr.LoadConfig(CONF->SkillConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init skill mgr OK!"));

    //初始化状态配置 内部有关联 要求放在加载技能配置之后
    iRet = m_stStatusMgr.LoadConfig(CONF->StatusConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init status mgr OK!"));

    //初始化秘宝配置 内部有关联 要求放在加载状态配置之后
    iRet = m_stTreasureMgr.LoadConfig(CONF->TreasureConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init treasure mgr OK!"));

    //怪物信息初始化 内部加强对技能配置的检查 需要放在初始化技能配置和状态配置之后
    iRet = m_stMonsterConfigMgr.LoadConfig(CONF->MonsterConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init monster config mgr OK!"));

    //初始化主角配置 内部加强对技能配置的检查 需要放在初始化技能配置和状态配置之后
    iRet = m_stRoleConfigMgr.LoadConfig(CONF->RoleConfigFilePath());
    CHECK_RETURN(iRet);

    //初始化伙伴配置 内部加强对技能配置的检查 需要放在初始化技能配置和状态配置之后 初始化任务配置之后
    iRet = m_stPartnerMgr.LoadConfig(CONF->PartnerConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init partner mgr OK!"));

    //初始化场景配置 内部有关联 要求放在加载怪物配置、物品配置之后
    iRet = m_stSceneMgr.LoadConfig(CONF->SceneConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init scene mgr OK!"));

    //初始化副本配置
    iRet = m_stInstanceMgr.LoadConfig(CONF->InstanceConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init instance mgr OK!"));

    //初始化卡片
    iRet = m_stCardMgr.LoadConfig(CONF->CardConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init card mgr OK!"));

    //初始化魂器
    iRet = m_stSoulMgr.LoadConfig(CONF->SoulConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init soul mgr OK!"));

    //初始化花园
    iRet = m_stGardenMgr.LoadConfig(CONF->GardenConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init garden mgr OK!"));

    //加载魔药配置
    iRet = m_stDrugMgr.LoadConfig(CONF->DrugConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init drug mgr OK!"));

    //初始化竞技场
    iRet = m_stArenaMgr.LoadConfig(CONF->ArenaConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init arena mgr OK!"));

    //初始化家族
    iRet = m_stClanMgr.LoadConfig(CONF->ClanConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init clan mgr OK!"));

    //家族神树配置
    iRet = m_stTreeMgr.LoadConfig(CONF->ClanConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init clan tree mgr OK!"));

    //初始化上课系统
    iRet = m_stLessonMgr.LoadConfig(CONF->LessonConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init lesson mgr OK!"));

    //初始化版署账号
    iRet = m_stSpecialAccountMgr.LoadConfig(CONF->SpecialAccountConfigFilePath());
    CHECK_RETURN(iRet);

    //初始化精力系统
    iRet = m_stEnergyMgr.LoadConfig(CONF->EnergyConfigFilePath());
    CHECK_RETURN(iRet);

    //初始化宠物系统配置
    iRet = m_stPetMgr.Init();
    CHECK_RETURN(iRet);
    iRet = m_stPetMgr.LoadConfig(CONF->PetConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init pet mgr OK!"));

    iRet = m_stCommConfigLoader.LoadCoolingConfig(CONF->CoolingConfigFilePath());
    CHECK_RETURN(iRet);

    //读取冥想配置
    iRet = m_stMuseMgr.LoadConfig(CONF->MuseConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init Muse mgr OK!"));

    //读取坐骑配置
    iRet = m_stMountMgr.LoadConfig(CONF->MountConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init m_stMountMgr mgr OK!"));

    //时装时装配置
    iRet = m_stFashionMgr.LoadConfig(CONF->FashionConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init m_stFasionMgr mgr OK!"));

    //vip配置载入
    iRet = m_stVipMgr.LoadConfig(CONF->VipConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init vip mgr OK!"));

    //金币价格配置载入
    iRet = m_stCommConfigLoader.LoadGoldPriceConfig(CONF->VipConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init gold price OK!"));

    //魔界门配置载入
    iRet = m_stMagicDoorMgr.LoadConfig(CONF->MagicDoorConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init magic door mgr OK!"));

    //学院配置
    iRet = m_stCommConfigLoader.LoadAcademyConfig(CONF->AcademyConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init academy config OK!"));

    //系统开启配置
    iRet = m_stCommConfigLoader.LoadSysOpenConfig(CONF->SysOpenConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init sys open config OK!"));

    //mod配置
    iRet = m_stCommConfigLoader.LoadModConfig(CONF->ModParaConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init mod config OK!"));

    //活动奖励配置 内部有关联 要求放在加载物品配置之后
    iRet = m_stWorldBossMgr.LoadConfig(CONF->RewardConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init reward config OK!"));

    //家族闯关配置载入
    iRet = m_stFamilyDiceMgr.LoadConfig(CONF->DiceConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init family dice mgr OK!"));

    //拍卖会配置.(必须放在物品和卡片配置后)
    iRet = m_stAuctionMgr.LoadConfig(CONF->AuctionConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init auction mgr OK!"));

    //许愿池配置
    iRet = m_stWishMgr.LoadConfig(CONF->ActConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init wish mgr OK!"));

    //好友配置
    iRet = m_stFriendMgr.LoadConfig(CONF->FriendConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init friend mgr OK!"));

    //大区配置
    iRet = m_stCommConfigLoader.LoadZoneConfig(CONF->ZoneConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init zone config OK!"));

    //跨域文件配置
    iRet = m_stCommConfigLoader.LoadCrossDomainConfig(CONF->CrossDomainCfgFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init cross domain config OK!"));

    //学院杯
    iRet = m_stAcaCupMgr.LoadConfig(CONF->AcademyConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init m_stAcaCupMgr OK!"));

    //任务集市
    iRet = m_stMarketMgr.LoadConfig(CONF->MarketConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init m_stAcaCupMgr OK!"));

    //从数据库获取初始数据
    CCmdMainctrlInitData* pstCmd = (CCmdMainctrlInitData*)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_MAINCTRL_INIT_DATA);
    if (!pstCmd)
    {
        CT_WARNING(("create cmd error"));
        return RET_SYSTEM_FAIL;
    }

    pstCmd->pstMainCtrl = this;
    iRet = pstCmd->Do(NULL);
    CHECK_RETURN(iRet);

    while(!m_bDataIsInited)
    {
        int iNowTime = time(NULL);
        m_stEpoll.WaitAndEvent(10);

        //CCmdMainctrlInitData会更新数据，所以需要检查数据刷新以及系统繁忙
        bool bIsSwap = false;
        m_stUpdateMgr.CheckUpdate(1, iNowTime);
        m_stUpdateMgr.CheckSwap(1, iNowTime, 50, &bIsSwap);

        CheckSysBusy();
        if (m_bIsSysBusy)
        {
            CT_INFO(("init data: sys busy"));
        }

        if (!m_bIsSysBusy)
        {
            // 处理异步命令队列中命令
            CT_CMDFACTORY->DoQueue();
        }

        static int iLastDumpTime = 0;
        if (iLastDumpTime != iNowTime && iNowTime % 15 == 0)
        {
            DumpStatInfo();
            iLastDumpTime = iNowTime;
        }
    }

    //修正时间
    m_iNow += m_stZoneOnlineData._stZoneData._iTimeOffSet;

    //初始化活动配置 内部有关联 要求放在加载场景配置、状态配置之后. (需要使用开服时间,需要放在从数据库获取初始数据后加载)
    iRet = m_stActMgr.LoadConfig(CONF->ActConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init act mgr OK!"));

    //需要使用开服时间,需要放在从数据库获取初始数据后加载
    iRet = m_stQQMallMgr.LoadConfig(CONF->QQMallConfigFilePath());
    CHECK_RETURN(iRet);
    CT_INFO(("START......init mall mgr OK!"));

    //初始化昵称hash
    for (int i = m_stPlayerSnsCommInfoHash.GetHeadIndex();
        i >= 0; i = m_stPlayerSnsCommInfoHash.GetNextIndex(i))
    {
        CRolenameKey stKey;
        CTSizeString<ROLENAME_LEN> szRolenameWithTail = m_stPlayerSnsCommInfoHash[i]._sRoleName;
        stKey.Init(szRolenameWithTail);

        int iIndex = m_stRolenameHash.HashAlloc(stKey);
        if (iIndex < 0)
        {
            CT_ERROR(("alloc from rolename hash error, ret %d", iIndex));
            return RET_SYS_ERROR;
        }
        CT_TRACE(("get role name %s, gid %u", szRolenameWithTail(), m_stPlayerSnsCommInfoHash[i]._uiGID));
        m_stRolenameHash[iIndex] = m_stPlayerSnsCommInfoHash[i]._uiGID;
    }
    CT_INFO(("START......init role name hash OK!"));

    iRet = m_stClanMgr.InitData();
    CHECK_RETURN(iRet);
    CT_INFO(("START......init clan mgr OK!"));

    iRet = m_stInstanceMgr.InitData(m_stZoneOnlineData._stZoneData._stInstanceZoneData);
    CHECK_RETURN(iRet);
    CT_INFO(("START......init instance zone data OK!"));

    //计算世界BOSS等级
    m_stArenaMgr.AccountWorldBossLevel();

    //初始化拍卖会数据
    iRet = m_stTreeMgr.InitData (m_stZoneOnlineData._stZoneData);
    if (iRet)
    {
        CT_ERROR(("m_stTreeMgr.Init: %d", iRet));
        return iRet;
    }

    //初始化拍卖会数据
    iRet = m_stAuctionMgr.InitData (m_stZoneOnlineData._stZoneData._stAuctionZoneData);
    if (iRet)
    {
        CT_ERROR(("m_stAuctionMgr.InitData: %d", iRet));
        return iRet;
    }

    iRet = m_stRankMgr.InitData ();
    if (iRet)
    {
        CT_ERROR(("m_stRankMgr.InitData: %d", iRet));
        return iRet;
    }

    for (int i = m_stPlayerSnsCommInfoHash.GetHeadIndex();
        i >= 0; i = m_stPlayerSnsCommInfoHash.GetNextIndex(i))
    {
        unsigned int uiGid = m_stPlayerSnsCommInfoHash[i]._uiGID;
        int iIndex = m_stClanMgr.GetClanIndexByClanId(m_stPlayerSnsCommInfoHash[i]._uiClanId);
        if (iIndex >= 0)
        {
            CClanEntry & stEntry = m_stClanMgr.GetClanEntry(iIndex);
            if (!stEntry.HasThisMember(uiGid))
            {
                CT_ERROR(("%d, %d, %d", uiGid, m_stPlayerSnsCommInfoHash[i]._uiClanId, stEntry._uiClanId));
            }
        }
    }

    return 0;
}

int CMainCtrl::SetOnline(unsigned int uiGID, CEnpNetHead& stHead, CPlatformInfo& stPlatformInfo)
{
    int iIndex = m_stPlayerOnlineDataHash.HashAlloc(uiGID);
    if (iIndex < 0)
    {
        CT_ERROR(("alloc from m_stPlayerOnlineDataHash error"));
        return ERROR_ONLINE_FULL;
    }
    CPlayerOnlineData & stPlayerOnlineData = m_stPlayerOnlineDataHash[iIndex];

    int iRet = m_stPlayerOnline.SetOnline(uiGID, stHead, m_iNow, stPlatformInfo);
    if (iRet)
    {
        CT_ERROR(("m_stPlayerOnline.SetOnline error"));
        return ERROR_ONLINE_FULL;
    }

    stPlayerOnlineData.Clear();
    stPlayerOnlineData._uiGID = uiGID;

    //OSS
    m_stOssMgr.TriggerLogin(uiGID, m_iNow);
    m_stCompassMgr.Login(uiGID);

    return 0;
}

int CMainCtrl::SetOffline(unsigned int uiGID, CEnpNetHead& stHead)
{
    if (!m_stPlayerOnline.CheckOnline(uiGID))
    {
        return 0;
    }

    CCmdLogout* pstCmd = (CCmdLogout*)CT_CMDFACTORY->CreateCmd(ID_MAIN_LOGOUT);
    if (!pstCmd)
    {
        CT_WARNING(("create cmd error"));
        return RET_SYSTEM_FAIL;
    }

    pstCmd->pstMainCtrl = this;
    pstCmd->_uiGID = uiGID;
    pstCmd->stEnpHead = stHead;
    pstCmd->Do(NULL);

    //OSS
    m_stOssMgr.TriggerLogout(uiGID, m_iNow);

    int iOnlineTime = 0;
    m_stPlayerOnline.GetUserOnlineTime (uiGID, m_iNow, iOnlineTime);
    m_stCompassMgr.Quit(uiGID, iOnlineTime);

    m_stPlayerOnline.SetOffline(uiGID, stHead.uiHandle);
    int iIndex = m_stPlayerOnlineDataHash.HashFind(uiGID);
    if (iIndex < 0)
    {
        CT_WARNING(("SetOffline , %u not find", uiGID));
        return ERROR_NO_SUCH_USER;
    }

    m_stPlayerOnlineDataHash.HashFreeByIndex(iIndex);

    return 0;
}

int CMainCtrl::SetOfflineOnePlayer(unsigned int uiGID)
{
    CEnpNetHead stHead;
    m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stHead);
    SetOffline(uiGID, stHead);
    return 0;
}

int CMainCtrl::SetOfflineAllPlayer()
{
    const CTArray<unsigned int, MAX_PLAYER_ONLINE>& astGIDArray =
        CMainCtrl::m_pstMain->m_stPlayerOnline.GetAllOnlineUser();

    for (int i = 0; i < astGIDArray.Size(); ++i)
    {
        SetOfflineOnePlayer(astGIDArray[i]);
    }
    return 0;
}

int CMainCtrl::CheckLogin(unsigned int uiGID, CEnpNetHead& stHead)
{
    if (!m_stPlayerOnline.CheckOnline(uiGID)) //online
    {
        return RET_NOT_LOGIN;
    }

    CEnpNetHead stHeadOnline;
    m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stHeadOnline);

    if (stHeadOnline.uiHandle != stHead.uiHandle)
    {
        return RET_NOT_LOGIN;
    }

    /*
    if (stPlayerOnlineData._iDefendValidInfo != 1)
    {
    if (m_iNow - stPlayerOnlineData._iLoginTime + stPlayerOnlineData._iDefTotalOnlineTime >= 10800)
    {
    return RET_DEFEND_OFFTIME;
    }
    }
    */
    //Defend check -----------end

    //todo: 以后放在账单记录里
    m_stOssMgr.TriggerAct(uiGID);

    return 0;
}

int CMainCtrl::NotifyOnlineUser(unsigned int uiGID, CTBuffer& stBuff)
{
    int iRet = 0;
    //获取用户登录信息
    CEnpNetHead stDestHead;
    iRet = m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stDestHead);
    if (iRet)
    {
        CT_WARNING(("online user not find %u", uiGID));
        return iRet;
    }

    iRet = AnswerToClient(stDestHead, stBuff);
    if (iRet)
    {
        CT_WARNING(("AnswerToClient ret %d", iRet));
        return iRet;
    }

    return 0;
}

int CMainCtrl::NotifyOnlineUser(unsigned int uiGID, int iCmd, ::google::protobuf::Message& stMsgBody, bool bFlushData)
{
    int iRet = 0;

    CEnpNetHead stEnpHead;
    iRet = m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stEnpHead);
    if (iRet)
    {
        CT_WARNING(("online user(%u) handle not find when deal cmd(%d)",
            uiGID, iCmd));
        return iRet;
    }

    m_stEncodeBuf.Clear();

    PBMsgHead stMsgHead;

    stMsgHead.set_version(0);
    stMsgHead.set_time(m_iNow);
    stMsgHead.set_msgact(0);
    stMsgHead.set_msgid(iCmd);
    stMsgHead.set_msgret(0);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);

    EncodePBMsg(stMsgHead, m_stEncodeBuf);
    EncodePBMsg(stMsgBody, m_stEncodeBuf);

    iRet = AnswerToClient(stEnpHead, m_stEncodeBuf, bFlushData);
    if (iRet)
    {
        CT_WARNING(("AnswerToClient ret %d", iRet));
        return iRet;
    }

    return 0;
}

int CMainCtrl::SendInnerCmd(unsigned int uiGID, int iCmd, ::google::protobuf::Message& stMsgReq, bool bNeedLogin)
{
    int iRet = 0;

    CEnpNetHead stEnpHead;
    if (bNeedLogin)
    {
        iRet = m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stEnpHead);
        CT_RETURN(iRet);
    }

    PBMsgHead stMsgHead;
    stMsgHead.set_version(0);
    stMsgHead.set_time(m_iNow);
    stMsgHead.set_msgact(0);
    stMsgHead.set_msgid(iCmd);
    stMsgHead.set_msgret(0);
    stMsgHead.set_msgtype(CSMsgDef::EMT_REQUEST);

    CTAsyncCmd* pstCmd = (CTAsyncCmd*) CT_CMDFACTORY->CreateCmd(iCmd);
    if (!pstCmd)
    {
        CT_WARNING(("factory create cmd(%d) failed.",iCmd));
        return 0;
    }

    m_stEncodeBuf.Clear();
    iRet = EncodePBMsg(stMsgReq, m_stEncodeBuf, false);
    CT_RETURN(iRet);

    CTCodeStream s;
    iRet = s.Attach(m_stEncodeBuf.GetUsedBuf(), m_stEncodeBuf.GetUsedLen());
    CT_RETURN(iRet);

    CCmdPBDoParam stParam((void*)this, stEnpHead, stMsgHead, s);
    iRet = pstCmd->Do(&stParam);

    return 0;
}

void CMainCtrl::ProcWhenNewDayBegin (int iNowTime)
{
    m_stQQMallMgr.ProcCrossDay(iNowTime);
}

#if 0
int CMainCtrl::SendInnerCmd(unsigned int uiGID, int iCmd,
                            CTSizeString<CSMsgDef::EMT_MAX_MSGLEN>& szMsgStr)
{
    int iRet = 0;

    CEnpNetHead stEnpHead;
    iRet = m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stEnpHead);
    CT_RETURN(iRet);

    PBMsgHead stMsgHead;
    stMsgHead.set_version(0);
    stMsgHead.set_time(m_iNow);
    stMsgHead.set_msgact(0);
    stMsgHead.set_msgid(iCmd);
    stMsgHead.set_msgret(0);
    stMsgHead.set_msgtype(CSMsgDef::EMT_REQUEST);

    CTAsyncCmd* pstCmd = (CTAsyncCmd*) CT_CMDFACTORY->CreateCmd(iCmd);
    if (!pstCmd)
    {
        CT_WARNING(("factory create cmd(%d) failed.",iCmd));
        return 0;
    }

    CTCodeStream s;
    iRet = s.Attach(szMsgStr(), szMsgStr.GetDataLen());
    CT_RETURN(iRet);

    CCmdPBDoParam stParam((void*)this, stEnpHead, stMsgHead, s);
    iRet = pstCmd->Do(&stParam);

    return 0;
}
#endif
template<>
void CMainCtrl::BeforeUpdateData<CPlayerSnsDetailInfo>(CPlayerSnsDetailInfo& stData)
{
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(stData._uiGID);
    if (iIndex < 0)
    {
        iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashAlloc(stData._uiGID);
    }

    if (iIndex >= 0)
    {
        CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iIndex] = stData._stCommInfo;
    }
    else
    {
        CT_WARNING(("gid = %u", stData._uiGID));
    }
}

template<>
void CMainCtrl::BeforeUpdateData<CPlayerBaseInfo>(CPlayerBaseInfo& stData)
{
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(stData._uiGID);
    if (iIndex < 0)
    {
        iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashAlloc(stData._uiGID);

        if (iIndex >= 0)
        {
            CPlayerSnsCommInfo& stCommInfo = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iIndex];
            stCommInfo._sRoleName = stData._sRoleName;
            stCommInfo._ucIsMale = stData._ucIsMale;
            stCommInfo._uiGID = stData._uiGID;
            stCommInfo._ucCareer = stData._ucCareer;
        }
    }
}

template<>
void CMainCtrl::BeforeUpdateData<CArenaRank>(CArenaRank& stData)
{
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsDetailInfoHash.HashFind(stData._uiGID);
    if (iIndex < 0)
    {
        CT_WARNING(("update arena rank , but not find sys detail info, %u", stData._uiGID));
    }
    else
    {
        CPlayerSnsDetailInfo& stPlayerSnsDetailInfo = CMainCtrl::m_pstMain->m_stPlayerSnsDetailInfoHash[iIndex];

        stPlayerSnsDetailInfo._stCommInfo._iArenaRank = stData._iNowRank;

        //保存数据
        CVarObjectKey stKey;
        stKey.m_chDataType = DT_PLAYER_SNS_DETAIL_INFO;
        stKey.m_u64ID = stData._uiGID;
        CMainCtrl::m_pstMain->UpdateData(stKey, stPlayerSnsDetailInfo, VOP_UPDATE);
    }
}

void CMainCtrl::CheckDef()
{
    CTArray<unsigned int, MAX_PLAYER_ONLINE> astGIDArray;
    m_stPlayerOnline.GetAllOnlineUser(astGIDArray);

    for (int i = 0; i < astGIDArray.Size(); ++i)
    {
        unsigned int uiGID = astGIDArray[i];
        int iIndex = m_stPlayerOnlineDataHash.HashFind(uiGID);
        if (iIndex < 0)
        {
            CT_WARNING(("%u", uiGID));
            continue;
        }
        CPlayerOnlineData& stPlayerOnlineData = m_stPlayerOnlineDataHash[iIndex];

        if (stPlayerOnlineData._iDefendValidInfo == 1) //已经通过身份验证
        {
            continue;
        }

        //检查累计在线时间
        int iDefOnlineTime = m_iNow - stPlayerOnlineData._iLoginTime + stPlayerOnlineData._iDefTotalOnlineTime;
        if (iDefOnlineTime < 10800) //没有被防沉迷
        {
            continue;
        }

        //发送消息
        PBMsgHead stMsgHead;
        stMsgHead.set_time(m_iNow);
        stMsgHead.set_msgid(ID_MAIN_GET_DEFEND_INFO);
        stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);
        stMsgHead.set_msgret(0);

        MsgAnsUserValidInfo stAns;
        stAns.set_gid(uiGID);
        stAns.set_valid_status(stPlayerOnlineData._iDefendValidInfo);
        stAns.set_warn_times(4);
        stAns.set_time_in_day(iDefOnlineTime);

        m_stEncodeBuf.Clear();
        EncodePBMsg(stMsgHead, m_stEncodeBuf);
        EncodePBMsg(stAns, m_stEncodeBuf);
        NotifyOnlineUser(uiGID, m_stEncodeBuf);
        FlushSendCache();

        //踢下线
        CEnpNetHead stEnpHead;
        m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stEnpHead);
        SetOffline(uiGID, stEnpHead);
    }
}

int CMainCtrl::NotifyAllOnlineUser (CTBuffer& stBuff)
{
    CTArray<unsigned int, MAX_PLAYER_ONLINE> astGIDArray;
    CMainCtrl::m_pstMain->m_stPlayerOnline.GetAllOnlineUser(astGIDArray);

    for (int i = 0; i < astGIDArray.Size(); i++)
    {
        unsigned int uiGID = astGIDArray[i];
        CEnpNetHead stHead;
        int iRet = m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stHead);
        if (iRet)
        {
            CT_WARNING(("%d, %d", uiGID, iRet));
        }

        iRet = AnswerToClient(stHead, stBuff, true);
        if (iRet)
        {
            CT_WARNING(("%d, %d", uiGID, iRet));
        }
    }

    return 0;
}

int CMainCtrl::FindPlayerCacheData(unsigned int uiGID)
{
    int iIndex = 0;
    iIndex = m_stPlayerCacheDataHash.HashFind(uiGID);
    if (iIndex < 0)
    {
        iIndex = m_stPlayerCacheDataHash.HashAlloc(uiGID);
        if (iIndex >= 0)
        {
            m_stPlayerCacheDataHash[iIndex]._uiGID = uiGID;
            return iIndex;
        }
        else
        {
            CT_ERROR(("role(%u) cache data alloc fail",uiGID));
        }
    }

    return iIndex;
}

void CMainCtrl::SendTips(unsigned int uiGID, CTips& stTips)
{
    CCmdSendTips* pstCmd = (CCmdSendTips*)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_SEND_TIPS);
    if (!pstCmd)
    {
        CT_WARNING(("create CCmdSendTips error"));
        return;
    }

    pstCmd->pstMainCtrl = this;
    pstCmd->_uiGID = uiGID;
    pstCmd->_stOfflineTips.AddTips(stTips);
    pstCmd->Do(NULL);
}

void CMainCtrl::SendTips(unsigned int uiGID, COfflineTips& stOfflineTips)
{
    CCmdSendTips* pstCmd = (CCmdSendTips*)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_SEND_TIPS);
    if (!pstCmd)
    {
        CT_WARNING(("create CCmdSendTips error"));
        return;
    }

    pstCmd->pstMainCtrl = this;
    pstCmd->_uiGID = uiGID;
    pstCmd->_stOfflineTips = stOfflineTips;
    pstCmd->Do(NULL);
}

void CMainCtrl::SendOnlineTips(unsigned int uiGID, MsgTips & stTips)
{
    PBMsgHead stMsgHead;
    stMsgHead.set_time(CMainCtrl::m_pstMain->m_iNow);
    stMsgHead.set_msgid(ID_MAIN_TIPS);
    stMsgHead.set_msgret(0);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stTips, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->NotifyOnlineUser(uiGID, CMainCtrl::m_pstMain->m_stEncodeBuf);
}

void CMainCtrl::SendOnlineTips(MsgTips& stTips)
{
    PBMsgHead stMsgHead;
    stMsgHead.set_time(CMainCtrl::m_pstMain->m_iNow);
    stMsgHead.set_msgid(ID_MAIN_TIPS);
    stMsgHead.set_msgret(0);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stTips, CMainCtrl::m_pstMain->m_stEncodeBuf);

    const CTArray<unsigned int, MAX_PLAYER_ONLINE>& astAllUsers = CMainCtrl::m_pstMain->m_stPlayerOnline.GetAllOnlineUser();

    for (int i = 0; i < astAllUsers.Size(); ++i)
    {
        CMainCtrl::m_pstMain->NotifyOnlineUser(astAllUsers[i], CMainCtrl::m_pstMain->m_stEncodeBuf);
    }
}

void CMainCtrl::HandleHttpMsg(CEnpNetHead& stNetHead, const char* szMsg, unsigned short usMsgLen)
{
#ifdef _DEBUG
    CTSizeString<65536> szShowHttpMsg;
    memcpy(szShowHttpMsg.GetBuffer(), szMsg, usMsgLen);
    szShowHttpMsg.SetDataLen(usMsgLen);
#endif

    CT_TRACE(("HandleHttpMsg: %s", szMsg));

    //获取请求类型
    const char* p1 = strchr(szMsg, '/');

    if (NULL == p1 || p1 - szMsg >= usMsgLen)
    {
        CT_ERROR((""));
        return;
    }
    p1++; //去掉'/'

    const char* p2 = strchr(p1, '?');
    if (NULL != p2 && p2 - szMsg > usMsgLen)
    {
        CT_ERROR((""));
        return;
    }

    if (NULL == p2 && p1 - szMsg < usMsgLen)
    {
        CTSizeString<50> szReqTypeTmp;
        int iTypeLenTmp = usMsgLen - (p1 - szMsg);

        const char* pX = strchr(p1, ' ');
        if (NULL != pX && pX - p1 < usMsgLen)
        {
            iTypeLenTmp = pX - p1;
        }

        if (iTypeLenTmp >= szReqTypeTmp.GetBufferLen())
        {
            CT_ERROR((""));
            return;
        }
        
        memcpy(szReqTypeTmp.GetBuffer(), p1, iTypeLenTmp);
        szReqTypeTmp.SetDataLen(iTypeLenTmp);

        if (0 == strcmp(szReqTypeTmp(), "crossdomain.xml"))
        {
            m_stEncodeBuf.Clear();
            CTSizeString<10240> szTmp2;
        
            szTmp2.Set("HTTP/1.0 200 OK\r\nContent-type: text/xml; charset=\"UTF-8\"\r\n\r\n%s",
                m_stCommConfigLoader._stCrossDomainConfig._szCrossDomain.Get());
        
            memcpy(m_stEncodeBuf.GetFreeBuf(), szTmp2(), szTmp2.GetDataLen());
            m_stEncodeBuf.Append(szTmp2.GetDataLen());
            AnswerToClient(stNetHead, m_stEncodeBuf, true);
        }

        return;//必须退出
    }

    if (strncmp(p1, "crossdomain", 11) == 0)
    {
        m_stEncodeBuf.Clear();
        CTSizeString<10240> szTmp2;
        
        szTmp2.Set("HTTP/1.0 200 OK\r\nContent-type: text/xml; charset=\"UTF-8\"\r\n\r\n%s",
            m_stCommConfigLoader._stCrossDomainConfig._szCrossDomain.Get());
        
        memcpy(m_stEncodeBuf.GetFreeBuf(), szTmp2(), szTmp2.GetDataLen());
        m_stEncodeBuf.Append(szTmp2.GetDataLen());
        AnswerToClient(stNetHead, m_stEncodeBuf, true);

        return;//必须退出
    }

    CTSizeString<50> szReqType;
    int iTypeLen = p2 - p1;
    if (iTypeLen >= szReqType.GetBufferLen())
    {
        CT_ERROR(("%s", p1));
        return;
    }

    memcpy(szReqType.GetBuffer(), p1, iTypeLen);
    szReqType.SetDataLen(iTypeLen);
    p2++; //去掉'?'

    //获取参数段
    int iParaLen = 0;

    const char* p3 = strstr(p2, "\r\n\r\n");
    if (p3)
    {
        iParaLen = p3 - p2;
    }
    else
    {
        CT_ERROR(("%s", szMsg));
        return;
    }

    if (iParaLen <= 0 || iParaLen + (p2 - szMsg) + 4 > usMsgLen) //4表示"\r\n\r\n"的长度
    {
        CT_ERROR(("%s", szMsg));
        return;
    }
#if 0
    const char* p4 = strchr(p2, ' '); //去掉无效的内容
    if (p4)
    {
        iParaLen = CT_MIN(iParaLen, p4 - p2);
    }
#endif
    //解析参数
    CTKeyValue stPara;
    stPara.ParseFromHttpParam(p2, iParaLen, false);

    CT_TRACE(("http type %s", szReqType()));
    for (int i = 0; i < stPara._astKeyValueData.Size(); ++i)
    {
        CT_TRACE(("%s=%s", stPara._astKeyValueData[i]._szKey(), stPara._astKeyValueData[i]._szValue()));
    }

    int iIndex = 0;
    bool bHasRole = false;

    //处理请求
    bool bSendMarketPay = false;   //是否为任务集市的发货
    if (0 == strcmp(szReqType(), "pay")) //支付发货
    {
        CTSizeString<USER_TOKEN_LEN> stUserToken;
        stUserToken.Set("%s", stPara.GetValue("openid"));

        iIndex = m_stUserTokenToGID.HashFind(stUserToken);
        if (iIndex < 0)
        {
            CT_ERROR(("%s", stUserToken()));
            return;
        }
        unsigned int uiGID = m_stUserTokenToGID[iIndex]._uiNowUseGID;
                
        if (0 == strcmp("0", stPara.GetValue("providetype")))
        {
            //普通购买发货
            CCmdSendGold* pstCmd = (CCmdSendGold*)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_SEND_GOLD);
            if (!pstCmd)
            {
                CT_WARNING(("create cmd error"));
                return;
            }

            pstCmd->pstMainCtrl = CMainCtrl::m_pstMain;
            pstCmd->stPara = stPara;
            pstCmd->stHead = stNetHead;
            pstCmd->uiGID = uiGID;
            pstCmd->Do(NULL);
        }
        else if (0 == strcmp("2", stPara.GetValue("providetype")))
        {
            bSendMarketPay = true;
        }
    }

    //任务集市
    if (0 == strcmp(szReqType(), "market") || bSendMarketPay)
    {
        //任务集市,奖励发放
        CCmdInnerMarket * pstCmd = (CCmdInnerMarket *)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_MARKET);
        if (!pstCmd)
        {
            CT_WARNING(("create cmd error"));
            return;
        }

        pstCmd->pstMainCtrl = CMainCtrl::m_pstMain;
        pstCmd->stHttpReqPara = stPara;
        pstCmd->stHead = stNetHead;

        if (bSendMarketPay)
        {
            pstCmd->_ucOpt = MARKET_OPT_AWARD_CALLBACK;
        }
        else
        {
            pstCmd->_ucOpt = MARKET_OPT_CHECK_COMPLETION;
        }        

        pstCmd->Do(NULL);        
    }
    else if (0 == strcmp(szReqType(), "user_exists"))
    {
        if (strlen(stPara.GetValue("platform")) <= 0)
        {
            CT_ERROR(("user_exists usertoken null!"));
        }

        CTSizeString<USER_TOKEN_LEN> szUserToken;
        if (IsTencentPlatform(stPara.GetValue("platform")))
        {
            if (strlen(stPara.GetValue("openid")) <= 0)
            {
                CT_ERROR(("user_exists openid null!"));
            }
            szUserToken.Set("%s", stPara.GetValue("openid"));
        }
        else if (IsLocalPlatform(stPara.GetValue("platform")))
        {
            if (strlen(stPara.GetValue("usertoken")) <= 0)
            {
                CT_ERROR(("user_exists usertoken null!"));
            }
            szUserToken.Set("%s", stPara.GetValue("usertoken"));
        }
        else
        {
            if (strlen(stPara.GetValue("username")) <= 0)
            {
                CT_ERROR(("user_exists usertoken null!"));
            }
            szUserToken.Set("%s", stPara.GetValue("username"));
        }

        iIndex = m_stUserTokenToGID.HashFind(szUserToken);

        bHasRole = false;
        if (iIndex >= 0 && m_stUserTokenToGID[iIndex]._auiGID.Size() > 0)
        {
            bHasRole = true;
        }

        m_stEncodeBuf.Clear();
        CTSizeString<1024> szTmp2;

        if (bHasRole)
        {
            szTmp2.Set("HTTP/1.1 200 OK\r\nContent-length:3\r\n\r\n1\r\n");
        }
        else
        {
            szTmp2.Set("HTTP/1.1 200 OK\r\nContent-length:3\r\n\r\n0\r\n");
        }

        memcpy(m_stEncodeBuf.GetFreeBuf(), szTmp2(), szTmp2.GetDataLen());
        m_stEncodeBuf.Append(szTmp2.GetDataLen());
        AnswerToClient(stNetHead, m_stEncodeBuf, true);
    }
    else if (0 == strcmp(szReqType(), "check_user_name")) //检查昵称是否合法
    {
        CCmdInnerCheckName* pstCmd =
            (CCmdInnerCheckName*)CT_CMDFACTORY->CreateCmd(
                ID_MAIN_INNER_CHECK_NAME);
        if (!pstCmd)
        {
            CT_WARNING(("create cmd error"));
            return;
        }

        pstCmd->pstMainCtrl = this;
        pstCmd->stEnpHead = stNetHead;
        pstCmd->_stPara = stPara;
        pstCmd->Do(NULL);
    }
    else if (0 == strcmp(szReqType(), "create_user")) //创建角色
    {
        CCmdInnerCreateUser* pstCmd =
            (CCmdInnerCreateUser*)CT_CMDFACTORY->CreateCmd(
                ID_MAIN_INNER_CREATE_USER);
        if (!pstCmd)
        {
            CT_WARNING(("create cmd error"));
            return;
        }

        pstCmd->pstMainCtrl = this;
        pstCmd->stEnpHead = stNetHead;
        pstCmd->_stPara = stPara;
        memcpy((void*)pstCmd->_szHttpStr.GetBuffer(), (void*)p2, iParaLen);
        pstCmd->_szHttpStr.SetDataLen(iParaLen);
        pstCmd->Do(NULL);
    }
    else if (0 == strcmp(szReqType(), "32wan_user_exists"))
    {
        //检查用户是否存在
        CTSizeString<USER_TOKEN_LEN> szUserToken;
        szUserToken.Set("%s", stPara.GetValue("userName"));
        iIndex = m_stUserTokenToGID.HashFind(szUserToken);

        bHasRole = false;
        if (iIndex >= 0 && m_stUserTokenToGID[iIndex]._auiGID.Size() > 0)
        {
            bHasRole = true;
        }

        m_stEncodeBuf.Clear();
        CTSizeString<10> szTmp;
        CTSizeString<1024> szTmp2;
        szTmp.Set("%d", bHasRole ? 1 : -1);
        szTmp2.Set("HTTP/1.0200OK\r\nContent-length:%d\r\n\r\n%s\r\n", szTmp.GetDataLen(), szTmp());
        
        memcpy(m_stEncodeBuf.GetFreeBuf(), szTmp2(), szTmp2.GetDataLen());
        m_stEncodeBuf.Append(szTmp2.GetDataLen());
        AnswerToClient(stNetHead, m_stEncodeBuf, true);
    }
    else if (0 == strcmp(szReqType(), "32wan_pay") || 0 == strcmp(szReqType(), "user_pay"))
    {
        int iResult = 0;

        do
        {
            //校验来源
            if (inet_addr(CONF->Wan32AdminHost.Get()) != 0 
                && inet_addr(CONF->Wan32AdminHost.Get()) != stNetHead.uiRemoteIP
                && inet_addr(CONF->Wan32AdminHost2.Get()) != stNetHead.uiRemoteIP)
            {
                iResult = -4;
                break;
            }

            //校验MD5
            string s;
            s += stPara.GetValue("orderNo");
            s += stPara.GetValue("passport");
            s += stPara.GetValue("serverId");
            s += stPara.GetValue("addGold");
            s += stPara.GetValue("rmb");
            s += stPara.GetValue("payTime");
            s += CONF->Wan32Key();
            if (0 != strcmp(CTMD5::GetMD5(s.c_str(), s.length()), stPara.GetValue("sign")))
            {
                iResult = -4;
                break;
            }

            //充值符石
            CTSizeString<USER_TOKEN_LEN> szUserToken;
            szUserToken.Set("%s", stPara.GetValue("passport"));

            unsigned int uiDestGID = 0;
            iIndex = m_stUserTokenToGID.HashFind(szUserToken);
            if (iIndex >= 0)
            {
                uiDestGID = m_stUserTokenToGID[iIndex]._uiNowUseGID;
            }
            
            if (uiDestGID == 0)
            {
                iResult = -2;
                break;
            }

            //发送金币
            CDropItem stDropItem;
            stDropItem.DropGold(atoi(stPara.GetValue("addGold")));
            stDropItem.SetOssModule(OSS_MODULE_32WAN_ADD_GOLD);
            m_stItemMgr.DropItem(NULL, uiDestGID, stDropItem, false);

            //TODO: 账单等


            iResult = 1;
        }while(0);

        //返回
        m_stEncodeBuf.Clear();
        CTSizeString<10> szTmp;
        CTSizeString<1024> szTmp2;
        szTmp.Set("%d", iResult);
        szTmp2.Set("HTTP/1.0200OK\r\nContent-length:%d\r\n\r\n%s\r\n", szTmp.GetDataLen(), szTmp());
        memcpy(m_stEncodeBuf.GetFreeBuf(), szTmp2(), szTmp2.GetDataLen());
        m_stEncodeBuf.Append(szTmp2.GetDataLen());
        AnswerToClient(stNetHead, m_stEncodeBuf, true);
    }
}

int CMainCtrl::GetDaysSinceZoneOpen ()
{
    int iZoneOpenTime = m_stZoneOnlineData._stZoneData._iZoneOpenTime;
    if (m_iNow < iZoneOpenTime)
    {
        CT_ERROR(("GetDaysSinceZoneOpen %d, %d", m_iNow, iZoneOpenTime));
        return 1;    //开服第一天
    }

    return ((m_iNow - iZoneOpenTime) / 86400 + 1);
}

int CMainCtrl::SendInnerCmd (CTAsyncCmd* pstParent, unsigned int uiGID, int iCmd, ::google::protobuf::Message& stMsgReq, bool bNeedLogin)
{
    int iRet = 0;

    CEnpNetHead stEnpHead;
    if (bNeedLogin)
    {
        iRet = m_stPlayerOnline.GetOnlinePlayerHandle(uiGID, stEnpHead);
        CT_RETURN(iRet);
    }

    PBMsgHead stMsgHead;
    stMsgHead.set_version(0);
    stMsgHead.set_time(m_iNow);
    stMsgHead.set_msgact(0);
    stMsgHead.set_msgid(iCmd);
    stMsgHead.set_msgret(0);
    stMsgHead.set_msgtype(CSMsgDef::EMT_REQUEST);

    CTAsyncCmd* pstCmd = (CTAsyncCmd*) CT_CMDFACTORY->CreateCmd(iCmd);
    if (!pstCmd)
    {
        CT_WARNING(("factory create cmd(%d) failed.",iCmd));
        return 0;
    }

    if (pstParent)
    {
        pstParent->RegisterChild(pstCmd);
    }

    m_stEncodeBuf.Clear();
    iRet = EncodePBMsg(stMsgReq, m_stEncodeBuf, false);
    CT_RETURN(iRet);

    CTCodeStream s;
    iRet = s.Attach(m_stEncodeBuf.GetUsedBuf(), m_stEncodeBuf.GetUsedLen());
    CT_RETURN(iRet);

    CCmdPBDoParam stParam((void*)this, stEnpHead, stMsgHead, s);
    iRet = pstCmd->Do(&stParam);

    return 0;
}

//腾讯帐号保活
void CMainCtrl::TencentKeepLoginValid(int iKeepValidSec)
{
    const CTArray<unsigned int, MAX_PLAYER_ONLINE>& auiAllOnlineUser = m_stPlayerOnline.GetAllOnlineUser();
    for (int i = 0; i < auiAllOnlineUser.Size(); ++i)
    {
        CPlatformInfo stPlatformInfo;
        if (m_stPlayerOnline.GetOnlinePlayerPlatformInfo(auiAllOnlineUser[i], stPlatformInfo)
            || !stPlatformInfo._bIsTencentLogin
            || (m_iNow - stPlatformInfo._iLastCheckLoginTime) < iKeepValidSec)
        {
            continue;
        }

        stPlatformInfo._iLastCheckLoginTime += iKeepValidSec;
        m_stPlayerOnline.SetOnlinePlayerPlatformInfo(auiAllOnlineUser[i], stPlatformInfo);

        //平台校验登录
        CCmdApi* pstCmd = (CCmdApi*)CT_CMDFACTORY->CreateCmd(ID_MAINAPI_TAPI);
        if (!pstCmd)
        {
            CT_ERROR(("create CCmdApi error"));
            break;
        }

        pstCmd->pstMainCtrl = this;
        pstCmd->stReq._ucReqType = CReqApi::API_TENCENT;
        pstCmd->stReq._szTApiName.Set("/v3/user/is_login");
        pstCmd->stReq._stTApiKeyValue = stPlatformInfo._stTencentApiKeyValue;
        pstCmd->Do(NULL);
    }
}

//////////////////////////////////////////////////////////////////////////
const unsigned char CShmCtrlInfo::abyMagicNum[MAGICNUM_LEN] = {32,54,69,54,25,87,24,65,122,32,1,8,95,144,15,22};

bool CShmCtrlInfo::CheckNeedReset(int iNowMenLen)
{
    //先判断长度，长度不一样，需要重新初始化
    if (iNowMenLen != m_iMemoryLen || memcmp(m_abyMagicNum, abyMagicNum, sizeof(abyMagicNum)) != 0)
    {
        return true;
    }
    return !m_bInitFinished;
}
//开始共享内存全部的初始化
void CShmCtrlInfo::StartFullInit(int iNowMenLen)
{
    //长度需要重值
    m_iMemoryLen = iNowMenLen;
    memcpy(m_abyMagicNum, abyMagicNum, sizeof(abyMagicNum));
    m_bInitFinished = false;
}
//结束共享内存的初始化
void CShmCtrlInfo::EndFullInit()
{
    m_bInitFinished = true;
}

bool CMainCtrl::IsTencentPlatform(const char* szPlatform)
{
    if (NULL == szPlatform)
    {
        CT_ERROR(("Platform String Null!"));
        return false;
    }

    CT_TRACE(("Platform %s", szPlatform));

    //qzone：空间；pengyou：朋友；qplus：Q+；tapp：微博；qqgame：QQGame；3366：3366；kapp：开心
    if (0 == strcmp(szPlatform, "qzone") || 0 == strcmp(szPlatform, "pengyou")
        || 0 == strcmp(szPlatform, "qqgame") || 0 == strcmp(szPlatform, "3366")
        || 0 == strcmp(szPlatform, "tapp") || 0 == strcmp(szPlatform, "website"))
    {
        return true;
    }

    return false;
}

bool CMainCtrl::IsLocalPlatform(const char* szPlatform)
{
    if (NULL == szPlatform)
    {
        CT_ERROR(("Platform String Null!"));
        return false;
    }

    CT_TRACE(("Platform %s", szPlatform));

    if (0 == strcmp(szPlatform, "local"))
    {
        return true;
    }

    return false;
}

